diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bc06555..309d8036 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -225,6 +225,59 @@ jobs: - name: Verify with Jolt verifier run: ./jolt-verifier/target/release/jolt-verifier --proof proof.bin --preprocessing preprocessing.bin + # ─── zolt-arith: differential fixture tests ──────────────────── + zolt-arith-diff: + name: Differential Fixture Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: mlugg/setup-zig@v2 + with: + version: ${{ env.ZIG_VERSION }} + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: tools/zolt-arith-diff/arkworks-fixtures + - name: Run differential tests + run: zig build test-zolt-arith-diff -Doptimize=ReleaseSafe + + # ─── zolt-arith: fixture freshness check ────────────────────── + zolt-arith-fixture-freshness: + name: Fixture Freshness Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: tools/zolt-arith-diff/arkworks-fixtures + - name: Regenerate fixtures + run: cargo run --release --manifest-path tools/zolt-arith-diff/arkworks-fixtures/Cargo.toml -- --out-dir testdata/zolt-arith-diff + - name: Check for drift + run: git diff --exit-code testdata/zolt-arith-diff/ + + # ─── zolt-arith: benchmark reporting (non-blocking) ─────────── + zolt-arith-bench: + name: Arithmetic Benchmarks + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + - name: Verify ARM64 + run: test "$(uname -m)" = "arm64" + - uses: mlugg/setup-zig@v2 + with: + version: ${{ env.ZIG_VERSION }} + - name: Field microbench + run: zig build bench-zolt-arith-field 2>&1 | tee field_bench.txt + - name: Pairing microbench + run: zig build bench-zolt-arith-pairing 2>&1 | tee pairing_bench.txt + - name: Post benchmark summary + run: | + echo "## Benchmark Results" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + grep '\[BENCH\]' field_bench.txt pairing_bench.txt >> $GITHUB_STEP_SUMMARY || true + echo '```' >> $GITHUB_STEP_SUMMARY + # ─── Gate: all required checks must pass ───────────────────────── ci-pass: name: CI Pass @@ -240,6 +293,8 @@ jobs: - cli-smoke - prove-verify-linux - prove-verify-macos + - zolt-arith-diff + - zolt-arith-fixture-freshness runs-on: ubuntu-latest steps: - name: Check all jobs @@ -255,6 +310,8 @@ jobs: "${{ needs.cli-smoke.result }}" "${{ needs.prove-verify-linux.result }}" "${{ needs.prove-verify-macos.result }}" + "${{ needs.zolt-arith-diff.result }}" + "${{ needs.zolt-arith-fixture-freshness.result }}" ) for r in "${results[@]}"; do if [[ "$r" != "success" ]]; then diff --git a/.gitignore b/.gitignore index c00f2633..7c13d6dc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ node_modules/ jolt/ bench/results/ jolt-bench/target/ +tools/zolt-arith-diff/arkworks-fixtures/target/ flamegraph.svg diff --git a/bench/zolt_arith/bench_harness.zig b/bench/zolt_arith/bench_harness.zig new file mode 100644 index 00000000..2bb19a80 --- /dev/null +++ b/bench/zolt_arith/bench_harness.zig @@ -0,0 +1,139 @@ +//! Shared microbenchmark harness for zolt-arith. +//! Zero external dependencies — only std. +//! +//! Usage: +//! const harness = @import("bench_harness.zig"); +//! _ = harness.run("field_Fp", "mul", harness.Config.field, Fp, struct { +//! fn call(i: usize) Fp { return a[i % N].mul(b[i % N]); } +//! }.call); + +const std = @import("std"); + +const MAX_SAMPLES: usize = 101; + +pub const Config = struct { + warmup_iters: usize = 5_000, + sample_count: usize = 21, + iters_per_sample: usize = 100_000, + + pub const field: Config = .{ + .warmup_iters = 20_000, + .sample_count = 21, + .iters_per_sample = 100_000, + }; + + pub const pairing: Config = .{ + .warmup_iters = 10, + .sample_count = 21, + .iters_per_sample = 100, + }; +}; + +pub const Stats = struct { + min_ns: f64, + median_ns: f64, + mean_ns: f64, + p99_ns: f64, + stddev_ns: f64, + sample_count: usize, + iters_per_sample: usize, +}; + +pub fn run( + comptime group: []const u8, + comptime op_name: []const u8, + config: Config, + comptime T: type, + comptime body: fn (usize) T, +) Stats { + const n = @min(config.sample_count, MAX_SAMPLES); + const iters = config.iters_per_sample; + + // --- Warmup --- + var sink: T = undefined; + for (0..config.warmup_iters) |i| { + sink = body(i); + } + std.mem.doNotOptimizeAway(&sink); + + // --- Collect samples --- + var samples: [MAX_SAMPLES]f64 = undefined; + for (0..n) |s| { + var timer = std.time.Timer.start() catch unreachable; + for (0..iters) |i| { + sink = body(i); + } + std.mem.doNotOptimizeAway(&sink); + const elapsed_ns: u64 = timer.read(); + samples[s] = @as(f64, @floatFromInt(elapsed_ns)) / + @as(f64, @floatFromInt(iters)); + } + + // --- Compute stats --- + const slice = samples[0..n]; + std.sort.pdq(f64, slice, {}, lessThanF64); + + var sum: f64 = 0; + for (slice) |v| sum += v; + const mean = sum / @as(f64, @floatFromInt(n)); + + var var_sum: f64 = 0; + for (slice) |v| { + const d = v - mean; + var_sum += d * d; + } + const stddev = @sqrt(var_sum / @as(f64, @floatFromInt(n))); + + const median = if (n % 2 == 1) + slice[n / 2] + else + (slice[n / 2 - 1] + slice[n / 2]) / 2.0; + + const p99_idx = @as(usize, @intFromFloat(@ceil(0.99 * @as(f64, @floatFromInt(n))))) - 1; + + const stats = Stats{ + .min_ns = slice[0], + .median_ns = median, + .mean_ns = mean, + .p99_ns = slice[p99_idx], + .stddev_ns = stddev, + .sample_count = n, + .iters_per_sample = iters, + }; + + printResult(group, op_name, stats); + return stats; +} + +fn lessThanF64(_: void, a: f64, b: f64) bool { + return a < b; +} + +const FmtTime = struct { val: f64, unit: []const u8 }; + +fn formatTime(ns: f64) FmtTime { + if (ns >= 1_000_000.0) return .{ .val = ns / 1_000_000.0, .unit = "ms" }; + if (ns >= 1_000.0) return .{ .val = ns / 1_000.0, .unit = "us" }; + return .{ .val = ns, .unit = "ns" }; +} + +fn printResult(comptime group: []const u8, comptime op_name: []const u8, s: Stats) void { + const min_f = formatTime(s.min_ns); + const med_f = formatTime(s.median_ns); + const mean_f = formatTime(s.mean_ns); + const p99_f = formatTime(s.p99_ns); + const sd_f = formatTime(s.stddev_ns); + + std.debug.print( + "[BENCH] group={s} op={s} min={d:.1}{s} median={d:.1}{s} mean={d:.1}{s} p99={d:.1}{s} stddev={d:.1}{s} samples={d}x{d}\n", + .{ + group, op_name, + min_f.val, min_f.unit, + med_f.val, med_f.unit, + mean_f.val, mean_f.unit, + p99_f.val, p99_f.unit, + sd_f.val, sd_f.unit, + s.sample_count, s.iters_per_sample, + }, + ); +} diff --git a/bench/zolt_arith/field_micro.zig b/bench/zolt_arith/field_micro.zig new file mode 100644 index 00000000..7f0e1a0b --- /dev/null +++ b/bench/zolt_arith/field_micro.zig @@ -0,0 +1,66 @@ +const std = @import("std"); +const zolt = @import("zolt"); +const harness = @import("bench_harness.zig"); + +const Fr = zolt.field.BN254Scalar; +const Fp = zolt.field.BN254BaseField; + +const ELEMENTS: usize = 128; + +fn fillFieldInputs(comptime F: type, a: *[ELEMENTS]F, b: *[ELEMENTS]F) void { + for (0..ELEMENTS) |i| { + a[i] = F.fromU64(@as(u64, @intCast(i + 1)) *% 0x9E3779B185EBCA87 +% 17); + b[i] = F.fromU64(@as(u64, @intCast(i + 1)) *% 0xD6E8FEB86659FD93 +% 29); + } +} + +fn benchField(comptime F: type, comptime field_name: []const u8) void { + const Ctx = struct { + var a: [ELEMENTS]F = undefined; + var b: [ELEMENTS]F = undefined; + + fn add(i: usize) F { + return a[i % ELEMENTS].add(b[i % ELEMENTS]); + } + fn sub(i: usize) F { + return a[i % ELEMENTS].sub(b[i % ELEMENTS]); + } + fn mul(i: usize) F { + return a[i % ELEMENTS].mul(b[i % ELEMENTS]); + } + fn square(i: usize) F { + return a[i % ELEMENTS].square(); + } + fn inverse(i: usize) F { + return a[i % ELEMENTS].inverse() orelse F.zero(); + } + fn toMontgomery(i: usize) F { + return a[i % ELEMENTS].fromMontgomery().toMontgomery(); + } + fn fromMontgomery(i: usize) F { + return a[i % ELEMENTS].fromMontgomery(); + } + fn sumOfProducts(i: usize) F { + const idx = i % ELEMENTS; + return F.sumOfProducts(.{ a[idx], b[idx] }, .{ b[idx], a[idx] }); + } + }; + + fillFieldInputs(F, &Ctx.a, &Ctx.b); + + const cfg = harness.Config.field; + _ = harness.run(field_name, "add", cfg, F, Ctx.add); + _ = harness.run(field_name, "sub", cfg, F, Ctx.sub); + _ = harness.run(field_name, "mul", cfg, F, Ctx.mul); + _ = harness.run(field_name, "square", cfg, F, Ctx.square); + _ = harness.run(field_name, "inverse", cfg, F, Ctx.inverse); + _ = harness.run(field_name, "toMontgomery", cfg, F, Ctx.toMontgomery); + _ = harness.run(field_name, "fromMontgomery", cfg, F, Ctx.fromMontgomery); + _ = harness.run(field_name, "sumOfProducts", cfg, F, Ctx.sumOfProducts); +} + +pub fn main() !void { + std.debug.print("=== Zolt-Arith Field Microbench ===\n", .{}); + benchField(Fp, "field_Fp"); + benchField(Fr, "field_Fr"); +} diff --git a/bench/zolt_arith/pairing_micro.zig b/bench/zolt_arith/pairing_micro.zig new file mode 100644 index 00000000..2e596421 --- /dev/null +++ b/bench/zolt_arith/pairing_micro.zig @@ -0,0 +1,75 @@ +const std = @import("std"); +const zolt = @import("zolt"); +const harness = @import("bench_harness.zig"); + +const field = zolt.field; +const p = field.pairing; + +const Fp = field.BN254BaseField; +const Fp12 = p.Fp12; +const G1PointFp = p.G1PointFp; +const G2Point = p.G2Point; +const G2Projective = p.G2Projective; +const G2Prepared = p.G2Prepared; + +const NUM_PAIRS: usize = 4; + +// File-scope inputs populated by setupInputs(). +var g1_points: [NUM_PAIRS]G1PointFp = undefined; +var g2_points: [NUM_PAIRS]G2Point = undefined; +var g2_prepared: [NUM_PAIRS]G2Prepared = undefined; +var fp12_inputs: [NUM_PAIRS]Fp12 = undefined; + +fn setupInputs() void { + // G1: use generator (1, 2) for all pairs — pairing cost is + // dominated by the fixed Miller loop, not the specific point. + for (0..NUM_PAIRS) |i| { + g1_points[i] = .{ .x = Fp.one(), .y = Fp.fromU64(2), .infinity = false }; + } + + // G2: varied points via repeated doubling of generator. + var g2_proj = G2Projective.fromAffine(G2Point.generator()); + for (0..NUM_PAIRS) |i| { + g2_points[i] = g2_proj.toAffine(); + g2_prepared[i] = G2Prepared.fromG2Point(g2_points[i]); + g2_proj = g2_proj.double(); + } + + // Fp12: Miller loop outputs for finalExponentiation benchmark. + for (0..NUM_PAIRS) |i| { + fp12_inputs[i] = p.millerLoopArkworks(g1_points[i], g2_points[i]); + } +} + +// --- Benchmark bodies --- + +fn benchPairingFp(i: usize) Fp12 { + const idx = i % NUM_PAIRS; + return p.pairingFp(g1_points[idx], g2_points[idx]); +} + +fn benchMillerLoop(i: usize) Fp12 { + const idx = i % NUM_PAIRS; + return p.millerLoopArkworks(g1_points[idx], g2_points[idx]); +} + +fn benchMillerLoopPrepared(i: usize) Fp12 { + const idx = i % NUM_PAIRS; + return p.millerLoopPrepared(g1_points[idx], &g2_prepared[idx]); +} + +fn benchFinalExp(i: usize) Fp12 { + const idx = i % NUM_PAIRS; + return p.finalExponentiation(fp12_inputs[idx]); +} + +pub fn main() !void { + std.debug.print("=== Zolt-Arith Pairing Microbench ===\n", .{}); + setupInputs(); + + const cfg = harness.Config.pairing; + _ = harness.run("pairing", "pairingFp", cfg, Fp12, benchPairingFp); + _ = harness.run("pairing", "millerLoop", cfg, Fp12, benchMillerLoop); + _ = harness.run("pairing", "millerLoopPrepared", cfg, Fp12, benchMillerLoopPrepared); + _ = harness.run("pairing", "finalExp", cfg, Fp12, benchFinalExp); +} diff --git a/build.zig b/build.zig index 903a1e9f..1ab27bf4 100644 --- a/build.zig +++ b/build.zig @@ -278,6 +278,38 @@ pub fn build(b: *std.Build) void { const field_bench_step = b.step("bench-field", "Run field arithmetic benchmark"); field_bench_step.dependOn(&run_field_bench.step); + // Benchmark: zolt-arith field microbench (repo-level, optional) + const zolt_arith_field_micro = b.addExecutable(.{ + .name = "zolt-arith-field-micro", + .root_module = b.createModule(.{ + .root_source_file = b.path("bench/zolt_arith/field_micro.zig"), + .target = target, + .optimize = .ReleaseFast, + .imports = &.{ + .{ .name = "zolt", .module = lib.root_module }, + }, + }), + }); + const run_zolt_arith_field_micro = b.addRunArtifact(zolt_arith_field_micro); + const zolt_arith_field_micro_step = b.step("bench-zolt-arith-field", "Run zolt-arith field microbench"); + zolt_arith_field_micro_step.dependOn(&run_zolt_arith_field_micro.step); + + // Benchmark: zolt-arith pairing microbench (repo-level, optional) + const zolt_arith_pairing_micro = b.addExecutable(.{ + .name = "zolt-arith-pairing-micro", + .root_module = b.createModule(.{ + .root_source_file = b.path("bench/zolt_arith/pairing_micro.zig"), + .target = target, + .optimize = .ReleaseFast, + .imports = &.{ + .{ .name = "zolt", .module = lib.root_module }, + }, + }), + }); + const run_zolt_arith_pairing_micro = b.addRunArtifact(zolt_arith_pairing_micro); + const zolt_arith_pairing_micro_step = b.step("bench-zolt-arith-pairing", "Run zolt-arith pairing microbench"); + zolt_arith_pairing_micro_step.dependOn(&run_zolt_arith_pairing_micro.step); + // Benchmark: ARM64 field verification const arm64_verify = b.addExecutable(.{ .name = "arm64-verify", @@ -310,6 +342,40 @@ pub fn build(b: *std.Build) void { const gpu_bench_step = b.step("bench-gpu", "Run GPU vs CPU benchmark"); gpu_bench_step.dependOn(&run_gpu_bench.step); + // Optional: differential fixture generation outside the package + const gen_zolt_arith_diff = b.addSystemCommand(&.{ + "cargo", + "run", + "--release", + "--manifest-path", + "tools/zolt-arith-diff/arkworks-fixtures/Cargo.toml", + "--", + "--out-dir", + "testdata/zolt-arith-diff", + }); + const gen_zolt_arith_diff_step = b.step("gen-zolt-arith-diff-fixtures", "Generate optional zolt-arith differential fixtures via arkworks"); + gen_zolt_arith_diff_step.dependOn(&gen_zolt_arith_diff.step); + + // Optional: differential fixture verification outside the package + const zolt_arith_diff_options = b.addOptions(); + zolt_arith_diff_options.addOption([]const u8, "fixtures_root", b.pathFromRoot("testdata/zolt-arith-diff")); + + const zolt_arith_diff_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("tools/zolt-arith-diff/check.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "zolt", .module = lib.root_module }, + .{ .name = "diff_config", .module = zolt_arith_diff_options.createModule() }, + }, + }), + }); + if (is_apple_silicon) linkMetalFrameworks(zolt_arith_diff_tests.root_module); + const run_zolt_arith_diff_tests = b.addRunArtifact(zolt_arith_diff_tests); + const zolt_arith_diff_step = b.step("test-zolt-arith-diff", "Run optional zolt-arith differential fixtures"); + zolt_arith_diff_step.dependOn(&run_zolt_arith_diff_tests.step); + // Optional: rebuild Metal shaders from .metal sources // Usage: zig build metal-shaders const metal_step = b.step("metal-shaders", "Rebuild Metal shader library from .metal sources"); diff --git a/docs/research/zolt-arith-test-benchmark-matrix.md b/docs/research/zolt-arith-test-benchmark-matrix.md new file mode 100644 index 00000000..c5a6160d --- /dev/null +++ b/docs/research/zolt-arith-test-benchmark-matrix.md @@ -0,0 +1,336 @@ +# Zolt-Arith Test And Benchmark Matrix + +**Date:** 2026-04-05 +**Scope:** `packages/zolt-arith/` +**Purpose:** Turn the package's already-large inline-test baseline into a fixture-backed, differential-tested, benchmarked verification target + +## Executive Summary + +`zolt-arith` is not starting from zero. + +Today it already has broad inline coverage across field arithmetic, pairings, transcripts, MSM, Dory, polynomial helpers, and GPU code. The package currently contains 203 `test` blocks under `packages/zolt-arith/src/`. + +The real gaps are elsewhere: + +- no broad checked-in external vector corpus yet +- no differential harness against `arkworks` or `gnark-crypto` +- no package-local microbenchmark entrypoints in `packages/zolt-arith/build.zig` +- no CI story for fixture freshness or benchmark regression tracking + +So the job is not "add tests" in the abstract. The job is: + +1. preserve the existing inline tests +2. add stable fixtures where exact bytes matter +3. add differential checks where BN254 behavior needs an oracle +4. standardize a benchmark workflow around the hot kernels that must not regress + +## Why This Exists + +The formal verification plan depends on a stable behavioral baseline and a stable performance baseline. + +That means `zolt-arith` needs two kinds of evidence before deeper proof work: + +- correctness evidence stronger than local unit/property tests alone +- performance evidence strong enough to reject proof-driven rewrites that slow down the field core + +This document is the concrete matrix for that work. + +## Current Baseline + +### Build and workflow reality + +Inside `packages/zolt-arith/`, [build.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/build.zig) currently exposes only: + +- `zig build test` + +There is currently: + +- an initial `src/testdata/` for field and transcript vectors +- no package-local +- `bench/` +- `tools/` +- benchmark step +- fixture generation step + +### Existing coverage that should be kept + +The package already has substantial inline tests. Highest-density areas include: + +| Area | Representative files | Current signal | +|---|---|---| +| Field core | [field/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/mod.zig), [field/accumulators.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/accumulators.zig) | arithmetic identities, inverse/power, sum-of-products, reduction paths, signed accumulation behavior | +| Pairings and extensions | [field/extensions.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/extensions.zig), [field/pairing.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/pairing.zig), [field/g2.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/g2.zig) | tower arithmetic, Miller-loop helpers, bilinearity, subgroup-related behavior | +| MSM | [msm/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/msm/mod.zig), [msm/glv.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/msm/glv.zig) | scalar multiplication, windowing, sequential/parallel behavior, GLV decomposition | +| Transcripts | [transcripts/blake2b.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/transcripts/blake2b.zig), [transcripts/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/transcripts/mod.zig) | determinism, challenge derivation, Jolt compatibility checks | +| Polynomial and subprotocol code | [poly/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/mod.zig), [poly/split_eq.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/split_eq.zig), [subprotocols/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/subprotocols/mod.zig) | eq-polynomial identities, binding behavior, sumcheck round logic | +| Commitment and serialization-adjacent code | [poly/commitment/dory.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/commitment/dory.zig), [poly/commitment/point_compression.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/commitment/point_compression.zig) | commitment consistency, serialization roundtrips, compressed-point behavior | +| GPU | [gpu/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/gpu/mod.zig), [gpu/field_ops.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/gpu/field_ops.zig), [gpu/poly_ops.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/gpu/poly_ops.zig), [gpu/msm_ops.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/gpu/msm_ops.zig) | CPU-vs-GPU correctness tests, with non-Metal targets compiling to stubs | + +### Existing benchmark machinery outside the package + +The repo already contains useful benchmark entrypoints that should be reused instead of replaced: + +- [bench/msm/main.zig](/Users/matteo/projects/zolt-code-review/bench/msm/main.zig) +- [bench/msm/bench_arkworks.rs](/Users/matteo/projects/zolt-code-review/bench/msm/bench_arkworks.rs) +- [bench/msm/compare.sh](/Users/matteo/projects/zolt-code-review/bench/msm/compare.sh) +- [bench/threadpool_vs_rayon/main.zig](/Users/matteo/projects/zolt-code-review/bench/threadpool_vs_rayon/main.zig) +- [bench/run-bench.sh](/Users/matteo/projects/zolt-code-review/bench/run-bench.sh) + +At the repo root, [build.zig](/Users/matteo/projects/zolt-code-review/build.zig) already exposes `bench-msm`, `bench-tp`, and `bench-scaling`. + +The package-local benchmark plan should standardize and narrow this existing machinery around `zolt-arith`, not duplicate it blindly. + +## Principles + +1. Keep the current inline tests and treat them as the first layer, not as throwaway coverage. +2. Add checked-in fixtures only where exact outputs matter across time and across implementations. +3. Use differential tests, not standards suites, for BN254-specific arithmetic and pairing behavior. +4. Benchmark the exact kernels that make the package fast, especially the field layer. +5. Keep GPU testing configuration-aware. + Metal execution is only real on macOS + Apple Silicon; on other targets the package intentionally compiles GPU stubs. +6. Do not pre-create a giant empty fixture tree. + Add directories and generator scripts only when they are tied to live tests. + +## External Sources Matrix + +Use external sources selectively: + +| Source | Use It For | Do Not Use It For | +|---|---|---| +| RFC 7693 BLAKE2 | exact transcript hashing fixtures for [transcripts/blake2b.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/transcripts/blake2b.zig) | BN254 arithmetic or pairing | +| Ethereum BN254 behavior | G1/G2/pairing compatibility and compressed-point behavior where Ethereum semantics are the target | complete field-kernel coverage | +| `arkworks` | primary differential oracle for field, extension field, G1/G2, MSM, pairings, and point compression | GPU-specific behavior | +| `gnark-crypto` | second independent oracle for field, extension field, and pairing behavior | GPU-specific behavior | +| Upstream Jolt fixtures or transcript behavior | transcript compatibility and protocol-level fixture capture | low-level limb arithmetic | +| Wycheproof | only if the package later adds standardized crypto APIs that actually map to it | BN254 field, pairing, MSM, Dory | +| NIST CAVP / ACVP | only if NIST-approved primitives are added later | BN254 field, pairing, MSM, Dory | + +## Coverage Gap Matrix + +The table below separates the current baseline from the missing work that matters for verification and performance preservation. + +### P0: must land first + +| Module | Current baseline | Missing addition | Benchmark target | Priority | +|---|---|---|---|---| +| [field/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/mod.zig) | inline algebraic tests, inverse/power, byte roundtrips, sum-of-products equivalence | checked-in BN254 scalar/base-field vectors plus `arkworks` differential corpus for exact arithmetic cases and Montgomery conversions | add/sub/mul/square/inverse, `toMontgomery`, `fromMontgomery` | P0 | +| [field/accumulators.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/accumulators.zig) | strong inline coverage for reductions, batch inversion, signed accumulators, stress cases | checked-in accumulator fixtures plus offline differential outputs for reducer paths | `reduceMulU64`, `reduceMulU128`, `sumOfProducts`, batch inversion | P0 | +| [transcripts/blake2b.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/transcripts/blake2b.zig) | determinism and Jolt-compat inline tests | RFC 7693 vectors plus checked-in transcript byte-for-byte fixtures captured from the compatibility target | append scalar/bytes, derive challenge | P0 | +| [msm/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/msm/mod.zig) | inline sequential/parallel and scalar-mul tests | checked-in small/medium vectors plus `arkworks` differential outputs for G1 Fr and G1 i128 cases | reuse [bench/msm/main.zig](/Users/matteo/projects/zolt-code-review/bench/msm/main.zig) and root `bench-msm` step | P0 | +| [field/pairing.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/pairing.zig) | bilinearity, identity, prepared/unprepared equivalence | Ethereum-compatible fixtures plus `arkworks` and `gnark-crypto` differential outputs for G1/G2/pairing tuples | Miller loop, final exponentiation, full pairing | P0 | + +### P1: next wave after the baseline is stable + +| Module | Current baseline | Missing addition | Benchmark target | Priority | +|---|---|---|---|---| +| [field/extensions.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/extensions.zig) | inline `Fp2/Fp6/Fp12` arithmetic and serialization tests | checked-in extension-field fixtures plus differential outputs from `arkworks` and `gnark-crypto` | `Fp2`, `Fp6`, `Fp12` mul/square/inverse | P1 | +| [field/g2.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/field/g2.zig) | inline G2 ops and scalar-mul consistency tests | generator/scalar-mul fixtures plus subgroup-focused differential cases | scalar multiplication and precompute helpers | P1 | +| [poly/commitment/point_compression.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/commitment/point_compression.zig) | inline roundtrip and identity tests | checked-in valid and invalid compressed encodings, ideally Ethereum-compatible where relevant | compression throughput and batch decompression | P1 | +| [msm/glv.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/msm/glv.zig) | inline decomposition and endomorphism tests | checked-in decomposition vectors from an offline oracle | decomposition overhead and end-to-end MSM win | P1 | +| [poly/commitment/g2_msm.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/commitment/g2_msm.zig) | indirect coverage via Dory paths | explicit small G2 MSM fixtures plus `arkworks` differential outputs | G2 MSM by Dory-relevant sizes | P1 | +| [poly/commitment/dory.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/commitment/dory.zig) | extensive inline behavior and serialization tests | stable checked-in small fixtures for commit/open/verify traces, especially where Jolt compatibility matters | commit/open by shape and pool mode | P1 | +| [gpu/field_ops.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/gpu/field_ops.zig), [gpu/poly_ops.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/gpu/poly_ops.zig) | inline CPU-vs-GPU correctness on Metal, stub behavior elsewhere | explicit crossover fixtures and benchmark reports by size; CI should treat non-Metal as compile-only for GPU paths | CPU vs GPU crossover points | P1 | + +### P2: useful, but lower leverage + +| Module | Current baseline | Missing addition | Benchmark target | Priority | +|---|---|---|---|---| +| [poly/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/mod.zig) | strong inline eq/binding coverage | checked-in tiny exact-value fixtures for deterministic regression coverage | `bindLow`, table builders, dense evaluate | P2 | +| [poly/interpolation.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/interpolation.zig) | a few inline interpolation tests | small exact interpolation fixtures and slow-scalar differential cases | interpolation by degree and size | P2 | +| [poly/product_tree.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/product_tree.zig) | indirect coverage | tiny exact fixtures | product tree scaling | P2 | +| [poly/split_eq.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/split_eq.zig), [poly/lt_poly.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/lt_poly.zig), [poly/multiquadratic.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/poly/multiquadratic.zig), [expanding_table.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/expanding_table.zig) | inline property tests already carry most of the value | optional exact fixtures if regressions appear repeatedly | helper microbenches only if they hit real profiles | P2 | +| [bits.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/bits.zig) | boundary and roundtrip inline tests | optional edge-case fixture file only if serialization consumers need byte stability | low priority | P2 | +| [transcripts/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/transcripts/mod.zig), [subprotocols/mod.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/src/subprotocols/mod.zig) | inline protocol-state checks | small deterministic fixture traces where protocol composition is fragile | protocol microbench only after field/MSM/pairing | P2 | + +## Test Types To Add + +For each P0 and P1 target, add only the test classes that buy unique signal: + +### 1. Golden vector tests + +Use checked-in fixtures for: + +- exact field input/output tuples +- exact transcript bytes and challenges +- exact compressed-point encodings +- exact small pairing tuples +- exact small MSM cases + +### 2. Differential tests + +Run the same fixture inputs against: + +- Zolt +- `arkworks` +- `gnark-crypto` where applicable + +Then compare exact outputs after normalization. + +This is the main oracle for BN254 arithmetic behavior. + +### 3. Property tests + +Keep and expand the existing property-style tests where they already fit well: + +- field algebraic laws +- serialization roundtrips +- sequential vs parallel equivalence +- CPU vs GPU equivalence +- prepared vs unprepared pairing equivalence + +### 4. Negative tests + +Add or strengthen negative cases for: + +- invalid compressed points +- malformed transcript labels or oversized packing inputs +- subgroup failures and invalid pairing inputs +- GPU unavailable-path behavior on non-Metal targets +- malformed or stale fixture decoding + +## Benchmark Matrix + +The benchmark plan should start from the hottest kernels and the already-existing repo entrypoints. + +### Immediate benchmark entrypoints to keep using + +- `zig build bench-msm` at the repo root +- [bench/msm/compare.sh](/Users/matteo/projects/zolt-code-review/bench/msm/compare.sh) for Zig-vs-arkworks MSM comparison +- `zig build bench-tp` +- `zig build bench-scaling` + +These are useful today even before package-local benchmark files exist. + +### Package-local benchmarks to add later + +Add package-local benches only once they have a clear owner and CI story: + +| Planned file | What it should measure | Why it matters | +|---|---|---| +| `packages/zolt-arith/bench/field_micro.zig` | field add/sub/mul/square/inverse, Montgomery conversions, reducers | hottest correctness-preserving perf gate | +| `packages/zolt-arith/bench/pairing_micro.zig` | Miller loop, final exponentiation, full pairing, prepared vs unprepared | protects expensive cryptographic core | +| `packages/zolt-arith/bench/poly_micro.zig` | `bindLow`, table builders, interpolation, product-tree helpers | useful only after field baseline is stable | +| `packages/zolt-arith/bench/transcript_micro.zig` | append scalar/bytes, derive challenge | low-cost guard for transcript-heavy call sites | +| `packages/zolt-arith/bench/gpu_compare.zig` | CPU vs GPU crossover by size | turns GPU claims into measured thresholds | + +### Metrics to capture + +Record at least: + +- ns/op +- ops/sec +- input size +- backend/configuration +- relative delta vs baseline + +Do not gate on exact absolute numbers. Gate on tolerance bands and regression percentages. + +## Proposed Minimal Repo Layout + +Do not create the full idealized tree up front. Start with the minimum directories that correspond to live tests and scripts. For fixtures that are embedded directly into Zig tests, the buildable location is under `src/testdata/`: + +```text +packages/zolt-arith/ +├── src/ +│ └── testdata/ +│ ├── field/ +│ ├── transcripts/ +│ ├── msm/ +│ └── pairing/ +├── bench/ +│ ├── field_micro.zig +│ └── pairing_micro.zig +└── tools/ + ├── gen_arkworks_vectors.rs + └── gen_gnark_vectors.go +``` + +Only after those are real should the package grow optional directories for: + +- extension-field vectors +- point-compression vectors +- Dory fixtures +- GPU crossover tools + +The scripts in `tools/` are offline generators. They are not trusted proof artifacts. Their job is only to emit stable checked-in fixtures. + +## Build And CI Rollout + +### Phase 1: no package build changes yet + +Land first: + +- `src/testdata/field/` +- `src/testdata/transcripts/` +- `src/testdata/msm/` +- `src/testdata/pairing/` +- test loaders and vector-driven tests inside the existing source files + +Keep running: + +- `cd packages/zolt-arith && zig build test` +- repo-root `zig build bench-msm` + +### Phase 2: add package-local benchmark steps + +Extend [packages/zolt-arith/build.zig](/Users/matteo/projects/zolt-code-review/packages/zolt-arith/build.zig) with: + +- `bench-field` +- `bench-pairing` + +Do not add `bench-poly`, `bench-transcript`, or fixture-regeneration steps until the first two are stable and clearly useful. + +### Phase 3: fixture freshness and differential verification + +Add CI jobs that: + +- run `zig build test` for the package +- verify checked-in fixtures still match the offline generators +- run differential fixture generation in a reproducible way +- publish benchmark deltas for the hot kernels + +### Blocking gates + +Start with non-blocking benchmark reporting. + +After the baseline is stable, promote only these to blocking perf checks: + +- field mul +- field reduction +- `sumOfProducts` +- Dory-relevant MSM sizes +- full pairing + +## Immediate Action Plan + +Implement in this order: + +1. Add checked-in fixtures for BN254 field ops, Blake2b RFC cases, Jolt transcript compatibility, small MSM cases, and small pairing cases. +2. Add vector-driven tests in the existing source files instead of creating a separate parallel test hierarchy. +3. Reuse root `bench-msm` as the first benchmark gate while adding package-local `field_micro.zig`. +4. Add offline vector generators for `arkworks` and `gnark-crypto`. +5. Extend the package build with `bench-field` and `bench-pairing` only after the first fixtures are in place. + +## Bottom Line + +The package already has meaningful inline coverage. The missing foundation is fixture-backed differential testing and a package-local benchmark workflow centered on the hot arithmetic path. + +For `zolt-arith`, the right baseline is: + +- keep the current inline tests +- add exact fixtures where bytes matter +- use `arkworks` and `gnark-crypto` as BN254 oracles +- reuse existing MSM benchmarking first +- add package-local field and pairing benches before lower-value benchmark work + +## Implementation Note + +The first differential implementation is intentionally repo-level and optional: + +- external fixtures live under `testdata/zolt-arith-diff/` +- the arkworks generator lives under `tools/zolt-arith-diff/` +- optional verification runs via `zig build test-zolt-arith-diff` +- optional generation runs via `zig build gen-zolt-arith-diff-fixtures` +- the dedicated field microbench runs via `zig build bench-zolt-arith-field` + +This keeps Rust and differential verification out of the normal `packages/zolt-arith` build while still making dedicated CI jobs straightforward. diff --git a/packages/zolt-arith/build.zig b/packages/zolt-arith/build.zig index d20072be..3ecec7d6 100644 --- a/packages/zolt-arith/build.zig +++ b/packages/zolt-arith/build.zig @@ -48,4 +48,16 @@ pub fn build(b: *std.Build) void { const run_tests = b.addRunArtifact(tests); const test_step = b.step("test", "Run zolt-arith tests"); test_step.dependOn(&run_tests.step); + + // Bench forwarding steps — actual executables live at repo root + // because they import the full `zolt` module. Run from repo root: + // zig build bench-zolt-arith-field + // zig build bench-zolt-arith-pairing + const bench_field_msg = b.addSystemCommand(&.{ "echo", "Run from repo root: zig build bench-zolt-arith-field" }); + const bench_field_step = b.step("bench-field", "Field arithmetic microbench (run from repo root)"); + bench_field_step.dependOn(&bench_field_msg.step); + + const bench_pairing_msg = b.addSystemCommand(&.{ "echo", "Run from repo root: zig build bench-zolt-arith-pairing" }); + const bench_pairing_step = b.step("bench-pairing", "Pairing microbench (run from repo root)"); + bench_pairing_step.dependOn(&bench_pairing_msg.step); } diff --git a/packages/zolt-arith/src/field/accumulators.zig b/packages/zolt-arith/src/field/accumulators.zig index d648b6b5..8ab49dbd 100644 --- a/packages/zolt-arith/src/field/accumulators.zig +++ b/packages/zolt-arith/src/field/accumulators.zig @@ -1297,8 +1297,8 @@ test "Barrett reduce round-trip: mulU64Unreduced STRESS (large N, large values)" for (0..N) |i| { // Use large field values (close to modulus) and large scalars - const w = BN254Scalar.fromU64(@as(u64, i) * 0x123456789ABCDEF + 0xFEDCBA9876543210); - const scalar: u64 = @as(u64, i) * 0xABCD + 0xFFFF; + const w = BN254Scalar.fromU64(@as(u64, i) *% 0x123456789ABCDEF +% 0xFEDCBA9876543210); + const scalar: u64 = @as(u64, i) *% 0xABCD +% 0xFFFF; folded.addAssign(mulU64Unreduced(w, scalar)); expected = expected.add(w.mul(BN254Scalar.fromU64(scalar))); } @@ -1313,8 +1313,8 @@ test "Barrett reduce round-trip: mulU64Unreduced STRESS (large N, large values)" var folded2 = FoldedMulU64.zero(); var expected2 = BN254Scalar.zero(); for (0..N) |i| { - const w = BN254Scalar.fromU64(@as(u64, i) * 0x123456789ABCDEF + 0xFEDCBA9876543210); - const scalar: u64 = @as(u64, i) * 0xABCD + 0xFFFF; + const w = BN254Scalar.fromU64(@as(u64, i) *% 0x123456789ABCDEF +% 0xFEDCBA9876543210); + const scalar: u64 = @as(u64, i) *% 0xABCD +% 0xFFFF; folded2.addAssign(mulU64Unreduced(w, scalar)); expected2 = expected2.add(w.mul(BN254Scalar.fromU64(scalar))); const check = reduceMulU64(folded2); diff --git a/packages/zolt-arith/src/field/g2.zig b/packages/zolt-arith/src/field/g2.zig index 12a30626..265112c9 100644 --- a/packages/zolt-arith/src/field/g2.zig +++ b/packages/zolt-arith/src/field/g2.zig @@ -879,3 +879,45 @@ test "batchInverseFp2 correctness" { try std.testing.expect(product.eql(Fp2.one())); } } + +test "g2 scalar mul fixture vectors" { + const testdata = @import("../testdata.zig"); + const fixture_text = @embedFile("../testdata/g2/g2_scalar_mul_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + const gen = G2Point.generator(); + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(8, line, '|'); + + const scalar_bytes = try testdata.parseHexBytesExact(32, fields[1]); + const scalar = BN254Scalar.fromBytesBE(&scalar_bytes); + const expected_infinity = try testdata.parseDecimal(u8, fields[3]); + + const actual = gen.scalarMul(scalar); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x_c0 = try testdata.parseHexBytesExact(32, fields[4]); + const expected_x_c1 = try testdata.parseHexBytesExact(32, fields[5]); + const expected_y_c0 = try testdata.parseHexBytesExact(32, fields[6]); + const expected_y_c1 = try testdata.parseHexBytesExact(32, fields[7]); + try std.testing.expectEqualSlices(u8, &expected_x_c0, &fpToBytesLE(actual.x.c0)); + try std.testing.expectEqualSlices(u8, &expected_x_c1, &fpToBytesLE(actual.x.c1)); + try std.testing.expectEqualSlices(u8, &expected_y_c0, &fpToBytesLE(actual.y.c0)); + try std.testing.expectEqualSlices(u8, &expected_y_c1, &fpToBytesLE(actual.y.c1)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 5); +} + +fn fpToBytesLE(value: Fp) [32]u8 { + const standard = value.fromMontgomery(); + var bytes: [32]u8 = undefined; + inline for (0..4) |i| { + std.mem.writeInt(u64, bytes[i * 8 ..][0..8], standard.limbs[i], .little); + } + return bytes; +} diff --git a/packages/zolt-arith/src/field/mod.zig b/packages/zolt-arith/src/field/mod.zig index fc6fa322..ac595774 100644 --- a/packages/zolt-arith/src/field/mod.zig +++ b/packages/zolt-arith/src/field/mod.zig @@ -5,6 +5,7 @@ const std = @import("std"); const builtin = @import("builtin"); +const testdata = @import("../testdata.zig"); /// LLVM carry/borrow intrinsics — map to single adc/sbb instructions on x86-64. /// Wrapped in a comptime-conditional struct so they are not emitted on non-x86 targets. @@ -176,6 +177,206 @@ pub inline fn arm64Sub192(a: [3]u64, b: [3]u64) [3]u64 { return .{ r0, r1, r2 }; } +/// ARM64 4-limb Montgomery squaring. +/// Computes a^2 * R^{-1} mod p using the "product then reduce" approach: +/// Phase 1: compute the 8-limb product a^2 using 10 muls (6 cross + 4 diagonal) +/// instead of the 16 muls required for generic a*b. +/// Phase 2: 4 iterations of Montgomery reduction (same as CIOS mul). +/// Total: ~52 multiply instructions vs ~68 for generic mul(a, a). +fn arm64MontgomerySquare256(a: *const [4]u64, mod: *const [4]u64, inv: u64) [4]u64 { + if (comptime builtin.cpu.arch != .aarch64) unreachable; + var r0: u64 = undefined; + var r1: u64 = undefined; + var r2: u64 = undefined; + var r3: u64 = undefined; + asm volatile ( + // ── Load a[0..3] into x4-x7 ── + \\ ldp x4, x5, [x13] + \\ ldp x6, x7, [x13, #16] + // + // ════════════════════════════════════════════════ + // Phase 1: Compute 8-limb product a^2 + // Strategy: compute cross products (i= 5); +} + test { // Run tests from sub-modules _ = extensions; diff --git a/packages/zolt-arith/src/msm/glv.zig b/packages/zolt-arith/src/msm/glv.zig index f1250593..84d8d1f5 100644 --- a/packages/zolt-arith/src/msm/glv.zig +++ b/packages/zolt-arith/src/msm/glv.zig @@ -621,3 +621,72 @@ test "glv scalar mul G2 larger scalar" { try std.testing.expect(glv_result.eql(naive_result)); } + +fn fpToBytesLE(value: Fp) [32]u8 { + const standard = value.fromMontgomery(); + var bytes: [32]u8 = undefined; + inline for (0..4) |i| { + std.mem.writeInt(u64, bytes[i * 8 ..][0..8], standard.limbs[i], .little); + } + return bytes; +} + +test "glv g1 scalar mul fixture vectors" { + const testdata = @import("../testdata.zig"); + const fixture_text = @embedFile("../testdata/glv/glv_g1_scalar_mul.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(5, line, '|'); + + const scalar_bytes = try testdata.parseHexBytesExact(32, fields[1]); + const scalar = Fr.fromBytesBE(&scalar_bytes); + const expected_infinity = try testdata.parseDecimal(u8, fields[2]); + + const actual = glvScalarMulG1(G1Affine.generator(), scalar).toAffine(); + + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x = try testdata.parseHexBytesExact(32, fields[3]); + const expected_y = try testdata.parseHexBytesExact(32, fields[4]); + try std.testing.expectEqualSlices(u8, &expected_x, &fpToBytesLE(actual.x)); + try std.testing.expectEqualSlices(u8, &expected_y, &fpToBytesLE(actual.y)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 8); +} + +test "glv g2 scalar mul fixture vectors" { + const testdata = @import("../testdata.zig"); + const fixture_text = @embedFile("../testdata/glv/glv_g2_scalar_mul.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(7, line, '|'); + + const scalar_bytes = try testdata.parseHexBytesExact(32, fields[1]); + const scalar = Fr.fromBytesBE(&scalar_bytes); + const expected_infinity = try testdata.parseDecimal(u8, fields[2]); + + const actual = glvScalarMulG2(G2Point.generator(), scalar).toAffine(); + + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x_c0 = try testdata.parseHexBytesExact(32, fields[3]); + const expected_x_c1 = try testdata.parseHexBytesExact(32, fields[4]); + const expected_y_c0 = try testdata.parseHexBytesExact(32, fields[5]); + const expected_y_c1 = try testdata.parseHexBytesExact(32, fields[6]); + try std.testing.expectEqualSlices(u8, &expected_x_c0, &fpToBytesLE(actual.x.c0)); + try std.testing.expectEqualSlices(u8, &expected_x_c1, &fpToBytesLE(actual.x.c1)); + try std.testing.expectEqualSlices(u8, &expected_y_c0, &fpToBytesLE(actual.y.c0)); + try std.testing.expectEqualSlices(u8, &expected_y_c1, &fpToBytesLE(actual.y.c1)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 8); +} diff --git a/packages/zolt-arith/src/msm/mod.zig b/packages/zolt-arith/src/msm/mod.zig index d4087ca9..bd6c1679 100644 --- a/packages/zolt-arith/src/msm/mod.zig +++ b/packages/zolt-arith/src/msm/mod.zig @@ -1897,3 +1897,123 @@ test "parallel msm zero scalars" { const result = try PMSM.compute(&bases, &scalars, 4, allocator); try std.testing.expect(result.isIdentity()); } + +// ============================================================================ +// Fixture-backed vector tests (arkworks-validated) +// ============================================================================ + +const testdata = @import("../testdata.zig"); + +fn fieldToBytesLE(comptime F: type, value: F) [32]u8 { + const standard = value.fromMontgomery(); + var bytes: [32]u8 = undefined; + inline for (0..4) |i| { + std.mem.writeInt(u64, bytes[i * 8 ..][0..8], standard.limbs[i], .little); + } + return bytes; +} + +test "msm g1 fr fixture vectors" { + const Fr = @import("../field/mod.zig").BN254Scalar; + const Fp = @import("../field/mod.zig").BN254BaseField; + const G1Affine = AffinePoint(Fp); + const G1MSM = MSM(Fr, Fp); + + const fixture_text = @embedFile("../testdata/msm/g1_fr_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + var case_count: usize = 0; + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(5, line, '|'); + + // Parse comma-separated BE hex scalars + var scalar_buf: [64]Fr = undefined; + var n: usize = 0; + var csv = std.mem.splitScalar(u8, fields[1], ','); + while (csv.next()) |token| { + const trimmed = std.mem.trim(u8, token, " \t\r"); + if (trimmed.len == 0) continue; + const bytes = try testdata.parseHexBytesExact(32, trimmed); + scalar_buf[n] = Fr.fromBytesBE(&bytes); + n += 1; + } + const scalars = scalar_buf[0..n]; + + // Generate bases: G, 2G, 4G, ... (successive doublings of generator) + var base_buf: [64]G1Affine = undefined; + const gen = G1Affine.generator(); + var proj = ProjectivePoint(Fp).fromAffine(gen); + for (0..n) |i| { + base_buf[i] = proj.toAffine(); + proj = proj.double(); + } + const bases = base_buf[0..n]; + + const actual = G1MSM.computeWithPool(bases, scalars, null); + const expected_infinity = try testdata.parseDecimal(u8, fields[2]); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x = try testdata.parseHexBytesExact(32, fields[3]); + const expected_y = try testdata.parseHexBytesExact(32, fields[4]); + const actual_x = fieldToBytesLE(Fp, actual.x); + const actual_y = fieldToBytesLE(Fp, actual.y); + try std.testing.expectEqualSlices(u8, &expected_x, &actual_x); + try std.testing.expectEqualSlices(u8, &expected_y, &actual_y); + } + case_count += 1; + } + try std.testing.expect(case_count >= 3); +} + +test "msm g1 i128 fixture vectors" { + const Fr = @import("../field/mod.zig").BN254Scalar; + const Fp = @import("../field/mod.zig").BN254BaseField; + const G1Affine = AffinePoint(Fp); + const G1MSM = MSM(Fr, Fp); + + const fixture_text = @embedFile("../testdata/msm/g1_i128_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + var case_count: usize = 0; + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(5, line, '|'); + + // Parse comma-separated decimal i128 scalars + var scalar_buf: [64]i128 = undefined; + var n: usize = 0; + var csv = std.mem.splitScalar(u8, fields[1], ','); + while (csv.next()) |token| { + const trimmed = std.mem.trim(u8, token, " \t\r"); + if (trimmed.len == 0) continue; + scalar_buf[n] = try std.fmt.parseInt(i128, trimmed, 10); + n += 1; + } + const scalars = scalar_buf[0..n]; + + // Generate bases: G, 2G, 4G, ... + var base_buf: [64]G1Affine = undefined; + const gen = G1Affine.generator(); + var proj = ProjectivePoint(Fp).fromAffine(gen); + for (0..n) |i| { + base_buf[i] = proj.toAffine(); + proj = proj.double(); + } + const bases = base_buf[0..n]; + + const actual = G1MSM.computeI128(bases, scalars, null); + const expected_infinity = try testdata.parseDecimal(u8, fields[2]); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x = try testdata.parseHexBytesExact(32, fields[3]); + const expected_y = try testdata.parseHexBytesExact(32, fields[4]); + const actual_x = fieldToBytesLE(Fp, actual.x); + const actual_y = fieldToBytesLE(Fp, actual.y); + try std.testing.expectEqualSlices(u8, &expected_x, &actual_x); + try std.testing.expectEqualSlices(u8, &expected_y, &actual_y); + } + case_count += 1; + } + try std.testing.expect(case_count >= 3); +} diff --git a/packages/zolt-arith/src/poly/commitment/g2_msm.zig b/packages/zolt-arith/src/poly/commitment/g2_msm.zig index a71e95d2..6f5a4e34 100644 --- a/packages/zolt-arith/src/poly/commitment/g2_msm.zig +++ b/packages/zolt-arith/src/poly/commitment/g2_msm.zig @@ -276,3 +276,64 @@ pub fn g2OptimalWindowSize(n: usize) usize { if (n < 131072) return 8; return 9; } + +fn fpToBytesLE(value: Fp) [32]u8 { + const standard = value.fromMontgomery(); + var bytes: [32]u8 = undefined; + inline for (0..4) |i| { + std.mem.writeInt(u64, bytes[i * 8 ..][0..8], standard.limbs[i], .little); + } + return bytes; +} + +test "g2 msm fr fixture vectors" { + const Fr = field.BN254Scalar; + const testdata = @import("../../testdata.zig"); + const fixture_text = @embedFile("../../testdata/msm/g2_fr_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields_split = try testdata.splitFieldsExact(7, line, '|'); + + // Parse comma-separated BE hex scalars + var scalar_buf: [64]Fr = undefined; + var n: usize = 0; + var csv = std.mem.splitScalar(u8, fields_split[1], ','); + while (csv.next()) |token| { + const trimmed = std.mem.trim(u8, token, " \t\r"); + if (trimmed.len == 0) continue; + const bytes = try testdata.parseHexBytesExact(32, trimmed); + scalar_buf[n] = Fr.fromBytesBE(&bytes); + n += 1; + } + const scalars = scalar_buf[0..n]; + + // Generate G2 bases: G2, 2*G2, 4*G2, ... (successive doublings) + var base_buf: [64]G2Point = undefined; + const gen = G2Point.generator(); + var proj = G2Projective.fromAffine(gen); + for (0..n) |i| { + base_buf[i] = proj.toAffine(); + proj = proj.double(); + } + const bases = base_buf[0..n]; + + const actual = msmG2Bench(Fr, bases, scalars, null); + const expected_infinity = try testdata.parseDecimal(u8, fields_split[2]); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x_c0 = try testdata.parseHexBytesExact(32, fields_split[3]); + const expected_x_c1 = try testdata.parseHexBytesExact(32, fields_split[4]); + const expected_y_c0 = try testdata.parseHexBytesExact(32, fields_split[5]); + const expected_y_c1 = try testdata.parseHexBytesExact(32, fields_split[6]); + try std.testing.expectEqualSlices(u8, &expected_x_c0, &fpToBytesLE(actual.x.c0)); + try std.testing.expectEqualSlices(u8, &expected_x_c1, &fpToBytesLE(actual.x.c1)); + try std.testing.expectEqualSlices(u8, &expected_y_c0, &fpToBytesLE(actual.y.c0)); + try std.testing.expectEqualSlices(u8, &expected_y_c1, &fpToBytesLE(actual.y.c1)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 3); +} diff --git a/packages/zolt-arith/src/poly/commitment/point_compression.zig b/packages/zolt-arith/src/poly/commitment/point_compression.zig index 689883ce..1aa2b0d2 100644 --- a/packages/zolt-arith/src/poly/commitment/point_compression.zig +++ b/packages/zolt-arith/src/poly/commitment/point_compression.zig @@ -265,59 +265,39 @@ pub fn decompressG2(bytes: *const [64]u8) ?G2Point { return G2Point.fromCoords(x, y); } -/// Compute square root in Fp2 -/// Uses the algorithm for extension fields +/// Compute square root in Fp2 = Fp[u]/(u^2 + 1). +/// +/// Given n = a + bu, finds x = c + du such that x^2 = n. +/// Uses the identity c^2 = (a + t)/2 where t = sqrt(a^2 + b^2), +/// with fallback to c^2 = (a - t)/2. Exactly one of these is a QR +/// in Fp when b != 0 (since p ≡ 3 mod 4 for BN254). pub fn fp2Sqrt(n: Fp2) ?Fp2 { if (n.isZero()) return Fp2.zero(); - // For Fp2 = Fp[u]/(u^2 + 1), we use a specialized algorithm. - // Let n = a + bu. We want to find x = c + du such that x^2 = n. - // (c + du)^2 = c^2 - d^2 + 2cdu = a + bu - // So: c^2 - d^2 = a, 2cd = b - - // If b = 0: n = a is in Fp, so sqrt(n) = sqrt(a) or sqrt(a)*u + // If b = 0: n = a is in Fp, so sqrt(n) = sqrt(a) or sqrt(-a)*u if (n.c1.isZero()) { - const sqrt_c0 = tonelliShanks(n.c0); - if (sqrt_c0) |s| { - return Fp2.init(s, Fp.zero()); - } - // Try sqrt(-a) * u - const neg_c0 = n.c0.neg(); - const sqrt_neg_c0 = tonelliShanks(neg_c0); - if (sqrt_neg_c0) |s| { - return Fp2.init(Fp.zero(), s); - } + if (tonelliShanks(n.c0)) |s| return Fp2.init(s, Fp.zero()); + if (tonelliShanks(n.c0.neg())) |s| return Fp2.init(Fp.zero(), s); return null; } - // General case: use the formula - // |n| = sqrt(a^2 + b^2) (in Fp, this is n * n.conjugate()) + // General case: t = sqrt(a^2 + b^2) in Fp const norm = n.c0.square().add(n.c1.square()); - const norm_sqrt = tonelliShanks(norm) orelse return null; - - // alpha = (a + |n|) / 2 + const t = tonelliShanks(norm) orelse return null; const two_inv = Fp.fromU64(2).inverse() orelse return null; - const alpha = n.c0.add(norm_sqrt).mul(two_inv); - const alpha_sqrt = tonelliShanks(alpha); - - if (alpha_sqrt) |c| { - // d = b / (2c) - const two_c_inv = c.add(c).inverse() orelse return null; - const d = n.c1.mul(two_c_inv); - return Fp2.init(c, d); - } - // Try the other case: alpha = (a - |n|) / 2 - const alpha2 = n.c0.sub(norm_sqrt).mul(two_inv); - const alpha2_sqrt = tonelliShanks(alpha2.neg()); - if (alpha2_sqrt) |d| { - // c = b / (2d) - const two_d_inv = d.add(d).inverse() orelse return null; - const c = n.c1.mul(two_d_inv); - return Fp2.init(c, d); + // Try c^2 = (a + t) / 2; if not QR, try c^2 = (a - t) / 2 + var x0 = n.c0.add(t).mul(two_inv); + var c_opt = tonelliShanks(x0); + if (c_opt == null) { + x0 = n.c0.sub(t).mul(two_inv); + c_opt = tonelliShanks(x0); } - return null; + // d = b / (2c) + const c = c_opt orelse return null; + const d = n.c1.mul(c.add(c).inverse() orelse return null); + return Fp2.init(c, d); } /// Check if Fp2 element a is "positive" compared to b @@ -399,21 +379,21 @@ test "g1 point compression identity" { } test "g2 point compression roundtrip" { - // Test with generator point - G2 decompression requires precise curve constants - // Skip the sqrt verification for now, just test that compression/decompression - // works for identity + // Identity roundtrip const identity = G2Point.identity(); const compressed_id = compressG2(identity); const decompressed_id = decompressG2(&compressed_id); try std.testing.expect(decompressed_id != null); try std.testing.expect(decompressed_id.?.infinity); - // For non-identity points, just verify compression produces correct format + // Generator roundtrip const g2_gen = G2Point.generator(); const compressed = compressG2(g2_gen); - - // Should have flag bits set correctly (not infinity) - try std.testing.expect(compressed[63] & 0x40 == 0); // Not infinity + const decompressed = decompressG2(&compressed); + try std.testing.expect(decompressed != null); + try std.testing.expect(!decompressed.?.infinity); + try std.testing.expect(decompressed.?.x.eql(g2_gen.x)); + try std.testing.expect(decompressed.?.y.eql(g2_gen.y)); } test "g2 point compression identity" { @@ -469,3 +449,139 @@ test "g2 compressed bytes for arkworks validation" { file.writeAll(&compressed_42) catch return; std.debug.print("Wrote 3 compressed G2 points to /tmp/zolt_g2_test_points.bin\n", .{}); } + +fn fpToBytesLE(value: Fp) [32]u8 { + const standard = value.fromMontgomery(); + var bytes: [32]u8 = undefined; + inline for (0..4) |i| { + std.mem.writeInt(u64, bytes[i * 8 ..][0..8], standard.limbs[i], .little); + } + return bytes; +} + +fn fpFromBytesLE(bytes: *const [32]u8) Fp { + var limbs: [4]u64 = undefined; + inline for (0..4) |i| { + limbs[i] = std.mem.readInt(u64, bytes[i * 8 ..][0..8], .little); + } + const raw = Fp{ .limbs = limbs }; + return raw.toMontgomery(); +} + +test "g1 point compression fixture vectors" { + const testdata = @import("../../testdata.zig"); + const fixture_text = @embedFile("../../testdata/point_compression/g1_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(4, line, '|'); + + const expected_compressed = try testdata.parseHexBytesExact(32, fields[3]); + + if (fields[1].len == 0) { + const identity = G1Point.identity(); + const actual_compressed = compressG1(identity); + try std.testing.expectEqualSlices(u8, &expected_compressed, &actual_compressed); + } else { + const x_bytes = try testdata.parseHexBytesExact(32, fields[1]); + const y_bytes = try testdata.parseHexBytesExact(32, fields[2]); + const point = G1Point{ .x = fpFromBytesLE(&x_bytes), .y = fpFromBytesLE(&y_bytes), .infinity = false }; + + const actual_compressed = compressG1(point); + try std.testing.expectEqualSlices(u8, &expected_compressed, &actual_compressed); + + const decompressed = decompressG1(&actual_compressed); + try std.testing.expect(decompressed != null); + try std.testing.expectEqualSlices(u8, &x_bytes, &fpToBytesLE(decompressed.?.x)); + try std.testing.expectEqualSlices(u8, &y_bytes, &fpToBytesLE(decompressed.?.y)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 6); +} + +test "g2 point compression fixture vectors" { + const testdata = @import("../../testdata.zig"); + const fixture_text = @embedFile("../../testdata/point_compression/g2_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(6, line, '|'); + + if (fields[1].len == 0) { + const identity = pairing.G2Point.identity(); + const compressed = compressG2(identity); + const decompressed = decompressG2(&compressed); + try std.testing.expect(decompressed != null); + try std.testing.expect(decompressed.?.infinity); + } else { + const x_c0 = try testdata.parseHexBytesExact(32, fields[1]); + const x_c1 = try testdata.parseHexBytesExact(32, fields[2]); + const y_c0 = try testdata.parseHexBytesExact(32, fields[3]); + const y_c1 = try testdata.parseHexBytesExact(32, fields[4]); + const point = pairing.G2Point{ + .x = Fp2.init(fpFromBytesLE(&x_c0), fpFromBytesLE(&x_c1)), + .y = Fp2.init(fpFromBytesLE(&y_c0), fpFromBytesLE(&y_c1)), + .infinity = false, + }; + + // Verify full compress → decompress roundtrip + const compressed = compressG2(point); + const decompressed = decompressG2(&compressed); + try std.testing.expect(decompressed != null); + try std.testing.expect(!decompressed.?.infinity); + try std.testing.expectEqualSlices(u8, &x_c0, &fpToBytesLE(decompressed.?.x.c0)); + try std.testing.expectEqualSlices(u8, &x_c1, &fpToBytesLE(decompressed.?.x.c1)); + try std.testing.expectEqualSlices(u8, &y_c0, &fpToBytesLE(decompressed.?.y.c0)); + try std.testing.expectEqualSlices(u8, &y_c1, &fpToBytesLE(decompressed.?.y.c1)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 6); +} + +test "fp2Sqrt correctness" { + const g2_gen = G2Point.generator(); + + // sqrt(y²) should give back ±y for the G2 generator + const y_squared = g2_gen.y.square(); + const sqrt_result = fp2Sqrt(y_squared); + try std.testing.expect(sqrt_result != null); + const s = sqrt_result.?; + try std.testing.expect(s.square().eql(y_squared)); + try std.testing.expect(s.eql(g2_gen.y) or s.eql(g2_gen.y.neg())); + + // sqrt(0) = 0 + const zero_sqrt = fp2Sqrt(Fp2.zero()); + try std.testing.expect(zero_sqrt != null); + try std.testing.expect(zero_sqrt.?.isZero()); + + // Pure real: sqrt(4) = 2 + const four = Fp2.init(Fp.fromU64(4), Fp.zero()); + const sqrt_four = fp2Sqrt(four); + try std.testing.expect(sqrt_four != null); + try std.testing.expect(sqrt_four.?.square().eql(four)); + + // Pure real, non-QR: sqrt(-1) should give purely imaginary result + const neg_one = Fp2.init(Fp.one().neg(), Fp.zero()); + const sqrt_neg_one = fp2Sqrt(neg_one); + try std.testing.expect(sqrt_neg_one != null); + try std.testing.expect(sqrt_neg_one.?.square().eql(neg_one)); + + // Multiple G2 points: verify y² roundtrip for [2]G, [42]G + const two_g = g2_gen.scalarMulU64(2); + const ys2 = two_g.y.square(); + const s2 = fp2Sqrt(ys2); + try std.testing.expect(s2 != null); + try std.testing.expect(s2.?.square().eql(ys2)); + + const g42 = g2_gen.scalarMulU64(42); + const ys42 = g42.y.square(); + const s42 = fp2Sqrt(ys42); + try std.testing.expect(s42 != null); + try std.testing.expect(s42.?.square().eql(ys42)); +} diff --git a/packages/zolt-arith/src/testdata.zig b/packages/zolt-arith/src/testdata.zig new file mode 100644 index 00000000..cfefd1ea --- /dev/null +++ b/packages/zolt-arith/src/testdata.zig @@ -0,0 +1,43 @@ +const std = @import("std"); + +pub fn cleanLine(raw_line: []const u8) ?[]const u8 { + const trimmed = std.mem.trim(u8, raw_line, " \t\r"); + if (trimmed.len == 0 or trimmed[0] == '#') return null; + return trimmed; +} + +pub fn splitFieldsExact(comptime field_count: usize, line: []const u8, separator: u8) ![field_count][]const u8 { + var parts = std.mem.splitScalar(u8, line, separator); + var fields: [field_count][]const u8 = undefined; + + for (0..field_count) |i| { + fields[i] = std.mem.trim(u8, parts.next() orelse return error.NotEnoughFields, " \t\r"); + } + + if (parts.next() != null) return error.TooManyFields; + return fields; +} + +pub fn parseDecimal(comptime T: type, text: []const u8) !T { + return std.fmt.parseInt(T, std.mem.trim(u8, text, " \t\r"), 10); +} + +pub fn parseHexBytesExact(comptime byte_len: usize, text: []const u8) ![byte_len]u8 { + const trimmed = std.mem.trim(u8, text, " \t\r"); + if (trimmed.len != byte_len * 2) return error.InvalidHexLength; + + var out: [byte_len]u8 = undefined; + _ = try std.fmt.hexToBytes(&out, trimmed); + return out; +} + +pub fn parseHexIntoBuffer(text: []const u8, buffer: []u8) ![]const u8 { + const trimmed = std.mem.trim(u8, text, " \t\r"); + if (trimmed.len % 2 != 0) return error.InvalidHexLength; + + const byte_len = trimmed.len / 2; + if (byte_len > buffer.len) return error.BufferTooSmall; + + _ = try std.fmt.hexToBytes(buffer[0..byte_len], trimmed); + return buffer[0..byte_len]; +} diff --git a/packages/zolt-arith/src/testdata/field/bn254_scalar_arithmetic.txt b/packages/zolt-arith/src/testdata/field/bn254_scalar_arithmetic.txt new file mode 100644 index 00000000..fecc5586 --- /dev/null +++ b/packages/zolt-arith/src/testdata/field/bn254_scalar_arithmetic.txt @@ -0,0 +1,4 @@ +# name|a_dec|b_dec|sum_dec|diff_dec|product_dec +small|7|3|10|4|21 +mid|100|50|150|50|5000 +large|12345678901234567890|9876543210987654321|22222222112222222211|2469135690246913569|121932631137021795223746380111126352690 diff --git a/packages/zolt-arith/src/testdata/field/bn254_scalar_serialization.txt b/packages/zolt-arith/src/testdata/field/bn254_scalar_serialization.txt new file mode 100644 index 00000000..ff831d76 --- /dev/null +++ b/packages/zolt-arith/src/testdata/field/bn254_scalar_serialization.txt @@ -0,0 +1,5 @@ +# name|value_dec|bytes_le_hex|bytes_be_hex +three|3|0300000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000003 +hundred|100|6400000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000064 +large_u64|12345678901234567890|d20a1feb8ca954ab000000000000000000000000000000000000000000000000|000000000000000000000000000000000000000000000000ab54a98ceb1f0ad2 +wide_u128|121932631137021795223746380111126352690|325316f72cf4d8015d104c65dc5ebb5b00000000000000000000000000000000|000000000000000000000000000000005bbb5edc654c105d01d8f42cf7165332 diff --git a/packages/zolt-arith/src/testdata/g2/g2_scalar_mul_vectors.txt b/packages/zolt-arith/src/testdata/g2/g2_scalar_mul_vectors.txt new file mode 100644 index 00000000..e684648b --- /dev/null +++ b/packages/zolt-arith/src/testdata/g2/g2_scalar_mul_vectors.txt @@ -0,0 +1,11 @@ +# G2 scalar mul vectors: [s]*G2_gen via arkworks +# name|scalar_be_hex|expected_infinity|expected_x_c0_le|expected_x_c1_le|expected_y_c0_le|expected_y_c1_le +scalar_mul|0000000000000000000000000000000000000000000000000000000000000000|-|1|||| +scalar_mul|0000000000000000000000000000000000000000000000000000000000000001|-|0|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018|c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19|aa7dfa6601cce64c7bd3430c69e7d1e38f40cb8d8071ab4aeb6d8cdba55ec812|5b9722d1dcdaac55f38eb37033314bbc95330c69ad999eec75f05f58d0890609 +scalar_mul|0000000000000000000000000000000000000000000000000000000000000002|-|0|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19 +scalar_mul|0000000000000000000000000000000000000000000000000000000000000003|-|0|f5c7fb9406fade12005e9ab08c477e8d5e7192e12628e551900eb14d784e0606|856eb6ff324f82c9a7a078686b1504bcebe4cf5dcd9151734297bb572f771410|97556c66576d036597e72145d720b969a0682c8d9c0f4b07e0b9b581561d8e05|b2117714caea2a454154b549ac5394dd23338df3c2fc2f92b74b35f335231e02 +scalar_mul|000000000000000000000000000000000000000000000000000000000000002a|-|0|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11|919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097412|72c1c48de52409d357618aac72fcbe14cf59f05162b5449630b6772e04416407|db41a40b7cd0c6a84e832bd28bc179c9c5ac040dd0ed7d4a6bf8e516982d2225 +scalar_mul|000000000000000000000000000000000000000000000000deadbeef12345678|-|0|31ace44ca364f4992a358bf050ef2935900504384d82397d07aaf9960a3ed21a|b7f71a7b71fe0a729c3f9863629d8bf99f6775751ff9df0afc528c67329c5914|1c9bbc4304eb9e58e811f06cc35b685aa12401d2d3d9bbe53f15f9a46767221a|9b8382f13ee704be4e8300c680a43117c9c983de0d3b4f53a067aeb8d1db6004 +scalar_mul|12642c234a2832bc1e466fd41b6e68e9a74b1cf50c3c2532f57d1b41135bff8e|-|0|d4365d8ba1ed23445a0e82a3c4d6f806fbd37f156b28df80fb0075b6d0b90a2c|6ebfebd591f8901b93faf518a43e4df2273e39974136a3e4b0e50092587c412b|abc06ba1ecc9c4b9ea171913538be2e346bc460c4a4836178905522d53ab6f1c|a466885c8df4607bc8bb6f5993c25d2312d1c3915777b3ed0745097ac1a39817 +scalar_mul|24b822660f774d469fb39393ed701b8a7b10a241878da51336a6df2803ea0fc6|-|0|1f4ab84c09edc218f8fb10ac60ead025e4083c916be4fbc9be0deb6377305205|36ef4e6ccb32eb11c14b62122e09ce44f58a4306f51ee6fac42ea52049883c0a|205d2a51872fbc18e95e65ed859b3d5da68bc74a3e72ec36421ee554041f9b05|f1955a06faf56b2d8c5a65a31d4061e21d1404ce3d88b087039d87075a42291a +scalar_mul|1a4e9bef842ed025cdf0d873bd15c251ca61a792db0f451e4c01ab1b30dd460a|-|0|efa676ad1a73c58ae22b5a321c7866e17906a33b2ff0fe236173267611021414|9b4f28ceea35fe9e87c6bf05ba8c2992b04c7a2481120534b756898ec4526f2b|ea419f2b0ccf19dd4b834fe3eb24bc8b59eecd1c1ea9129a61518e698e881700|4ff22fb055eb1e8733962d82a7e5379b5be9d313e06e4b3a185a46bff8682a06 diff --git a/packages/zolt-arith/src/testdata/glv/glv_g1_scalar_mul.txt b/packages/zolt-arith/src/testdata/glv/glv_g1_scalar_mul.txt new file mode 100644 index 00000000..7968bfb5 --- /dev/null +++ b/packages/zolt-arith/src/testdata/glv/glv_g1_scalar_mul.txt @@ -0,0 +1,11 @@ +# name|scalar_be_hex|expected_infinity|expected_x_le|expected_y_le +zero|0000000000000000000000000000000000000000000000000000000000000000|1|| +one|0000000000000000000000000000000000000000000000000000000000000001|0|0100000000000000000000000000000000000000000000000000000000000000|0200000000000000000000000000000000000000000000000000000000000000 +two|0000000000000000000000000000000000000000000000000000000000000002|0|d3cf876dc108c2d3a81c8716a91678d9851518685b04859b021a132ee7440603|c4a2185a7abf3effc78f53e349a4a6680a9caeb2965f84e7927c0a0e8c73ed15 +small_42|000000000000000000000000000000000000000000000000000000000000002a|0|343f9b2f88177d5317c5e46d58775535fbf727aefd9a8f7cd71f97b65df38809|03b24c3b2cdb2cd50599f495aea8b46028d56d6e0a390770c6c8af3fa6ffba23 +medium|000000000000000000000000000000000000000000000000deadbeef12345678|0|682f5ea7383ae04e80483382e36f9c64140be65660412b684c31e5c761c9c916|2c1955bdd1b88609d7794dde95de31dde662ecc0eb949a4de99f5c047b305b01 +random_0|1d147721e8f60b45009109e680d0d5d5c723fb9c79adc76dc6ae5c216db95521|0|c8b206be7c0d8524a73a0202fb5fdb05da66d65966ef54060f9f2a05443bb104|639190b6f455628014d02ec6a3fa16501634e519b83a1d4c762876a16ffc0f2f +random_1|279a1640c54a35fed39e6e9822a1671aae14ec8fab53a6cfdaa6e976e9267e40|0|5cc78fd4268ae35ff475446c563d4088434e21f74e25d7d398c6cd179714ce17|30acc9e8af7274799c744b96fccb1bd4e5cf31d13ecf46915e36ea459fabd82e +random_2|0c67fab0b8d427e7ac2d47859d8dfeea956f41f2f622ea509e39f1898d909c2d|0|0adf00494728e01994f492d0ad942d64c4f6f831b6f489a386c8a24fedae5c1a|557044be98c91acd73d77f6c90d57b4f9a3c605b86e1e7978f6af01557dadc27 +random_3|1efba8eabb15580c6e0e440965a9870477f607ab8918f217fe75b7980e70129e|0|fb6403bbbbee9780b8665726cd5f1b3f187774e586c57ff7f0786f24ba49492e|3f00d98222cd47abc4d6e0acab54b7ce15d30f7b97326a78596c9d5894d85202 +random_4|0eb059d41da72313c6dbd2bf01b3a68ffe93ea4ab1fb93352e3c055c8643b33f|0|cc88e464475a1041b8e4b08d6ac68331bbf8417d567d06bc7816560a11b3650e|902155d8c7e8f07c52ba0900957deefa75db36c68ee36a0541d5e63292743e20 diff --git a/packages/zolt-arith/src/testdata/glv/glv_g2_scalar_mul.txt b/packages/zolt-arith/src/testdata/glv/glv_g2_scalar_mul.txt new file mode 100644 index 00000000..5148fe4b --- /dev/null +++ b/packages/zolt-arith/src/testdata/glv/glv_g2_scalar_mul.txt @@ -0,0 +1,11 @@ +# name|scalar_be_hex|expected_infinity|expected_x_c0_le|expected_x_c1_le|expected_y_c0_le|expected_y_c1_le +zero|0000000000000000000000000000000000000000000000000000000000000000|1|||| +one|0000000000000000000000000000000000000000000000000000000000000001|0|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018|c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19|aa7dfa6601cce64c7bd3430c69e7d1e38f40cb8d8071ab4aeb6d8cdba55ec812|5b9722d1dcdaac55f38eb37033314bbc95330c69ad999eec75f05f58d0890609 +two|0000000000000000000000000000000000000000000000000000000000000002|0|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19 +small_42|000000000000000000000000000000000000000000000000000000000000002a|0|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11|919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097412|72c1c48de52409d357618aac72fcbe14cf59f05162b5449630b6772e04416407|db41a40b7cd0c6a84e832bd28bc179c9c5ac040dd0ed7d4a6bf8e516982d2225 +medium|000000000000000000000000000000000000000000000000deadbeef12345678|0|31ace44ca364f4992a358bf050ef2935900504384d82397d07aaf9960a3ed21a|b7f71a7b71fe0a729c3f9863629d8bf99f6775751ff9df0afc528c67329c5914|1c9bbc4304eb9e58e811f06cc35b685aa12401d2d3d9bbe53f15f9a46767221a|9b8382f13ee704be4e8300c680a43117c9c983de0d3b4f53a067aeb8d1db6004 +random_0|1d147721e8f60b45009109e680d0d5d5c723fb9c79adc76dc6ae5c216db95521|0|5e78318d6717b7f4b66d2c97f7c807c88f067e8c67fa642f263009300e1aae26|f6fa67c4b85d3823bcdce86c8a2d194005b77ac86650a0d0f9143976b29b8810|6e3edc2f9f72487d93da71ad933adf14bec4b14a6962338252e3e493367e5816|936799acf6f44c8ac55362fded926350631b35e7fba3f6d658c164fe91f0ae0d +random_1|279a1640c54a35fed39e6e9822a1671aae14ec8fab53a6cfdaa6e976e9267e40|0|4f6634066ead5df8b4a46df75cf7f07ebcfafa527e9b839c5b9b71b89e467e1c|9fa14c082d3f66eb5c0e88c64384bb1b539e546cb1c7459ae59dbde311e75927|b2cbabb4e6d821697a3f4e939a69257f56555b307e92df58bf4671e314d86c0f|b8a1be24743d8f4bfbb62fb819803d34bde7124c960c84f5bb82cad2d277c407 +random_2|0c67fab0b8d427e7ac2d47859d8dfeea956f41f2f622ea509e39f1898d909c2d|0|513385bdb5bf7a18edb5c75fdcef9593411e6be2cd9cf23272654c67d5619d05|44b6f63a004e19ffa6c1bcc6fe5c97f6dfdc3c54363c30b7ba582dc5af95be13|f390c407e0df6b126f4ad88b5f8d4b43a1a47680c10ea835225abd6d36b2b81d|8e60be5dd67d44f855baf025cec3be2400f6df4ac8731e697c80e6e0d7c48c07 +random_3|1efba8eabb15580c6e0e440965a9870477f607ab8918f217fe75b7980e70129e|0|ed093a5ec5886814ebba6b0aaba6ed0707ae131a915805c3f4a0a4356d576f14|aef9cc82ea969633edff5d9e59402433fdfe2357a5890eb4029c5572be548a1f|a6fd432d30706442cb051a6ca54cb1e87c934f157efcc1cc5aff9504a7428215|683e1607d445f2848d7e2d76cffe7f068951a15f7da8c521d090b40915b0f11a +random_4|0eb059d41da72313c6dbd2bf01b3a68ffe93ea4ab1fb93352e3c055c8643b33f|0|5ed8b04e9162b649680ee47e4fc4e6e6758280aee506cac28d1cfaf0d756b118|a8ea4edac5195c030bc6a375c875c9ee56fc138fd313afa1ff0bcace8167061e|152b2d972bc295e1bf4cf9ca3313cb4a3c9347122d18d496aaf4ff94b2e4f209|a30377151a6c04eb3210969c13cb59a73de226a6f1168799c31d609db81f5424 diff --git a/packages/zolt-arith/src/testdata/msm/g1_fr_vectors.txt b/packages/zolt-arith/src/testdata/msm/g1_fr_vectors.txt new file mode 100644 index 00000000..f3314027 --- /dev/null +++ b/packages/zolt-arith/src/testdata/msm/g1_fr_vectors.txt @@ -0,0 +1,5 @@ +# MSM G1 Fr vectors: bases are successive doublings of the BN254 G1 generator. +# name|scalars_be_hex_csv|expected_infinity|expected_x_le_hex|expected_y_le_hex +case_0|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49|0|f8faa90efa8b0bd3bf39e925d4b88082f442e1358fa658266876599aaae34406|8542f190037b02c6442904936b82ec3f50b520e88e599203f6262606f593de11 +case_1|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233|0|00920062e578e8a5b30042336de2380347bdbf67063d697d5ba5d172b5e59b0e|31bc9899678abf7f2feeb85b11f869a25e37d382e56f6e117ec524dbbf81b325 +case_2|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681|0|b2e690229aa4b5e13ac358cb35bbda40125b5f56f71313cccdeece9e1bfe6527|a3ec5d2452cdff746b41ea928064329f25a12cb51add3d353b4d769278e70416 diff --git a/packages/zolt-arith/src/testdata/msm/g1_i128_vectors.txt b/packages/zolt-arith/src/testdata/msm/g1_i128_vectors.txt new file mode 100644 index 00000000..2b44672b --- /dev/null +++ b/packages/zolt-arith/src/testdata/msm/g1_i128_vectors.txt @@ -0,0 +1,5 @@ +# MSM G1 i128 vectors: bases are successive doublings of the BN254 G1 generator. +# name|scalars_i128_csv|expected_infinity|expected_x_le_hex|expected_y_le_hex +case_0|-3432184301748786869|0|247045f0c624cf65f0914807fec5342413a2a2034b390e88c378af370c937321|2f88c8f11911bf2e28a1a38365c8111582837c4026c168e7ef3c5fa1ff580114 +case_1|-3432184301748786869,5465917739609016864|0|e09c4d8a2a98d962cee2d24d38caadcf4a08484e131a7f72ae393fbc4b72e62e|b9372bf08cd6b8886de630ceea89eab428525b58599f46a3423b73c6b1b0101c +case_2|-3432184301748786869,5465917739609016864,-8931409406514358511,444449029250614354|0|fef2828173c83484eb2a9d175fabe0b233915d433e88cd426e0b1561a4dc1028|3580f98a96cd14a586637713d52717155891eaced7f4103b908044f528119900 diff --git a/packages/zolt-arith/src/testdata/msm/g2_fr_vectors.txt b/packages/zolt-arith/src/testdata/msm/g2_fr_vectors.txt new file mode 100644 index 00000000..47f63252 --- /dev/null +++ b/packages/zolt-arith/src/testdata/msm/g2_fr_vectors.txt @@ -0,0 +1,7 @@ +# name|scalars_be_hex_csv|expected_infinity|expected_x_c0_le_hex|expected_x_c1_le_hex|expected_y_c0_le_hex|expected_y_c1_le_hex +case_0|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49|0|b1de020b72dd3e9010048b86f8faedfbbad05ceb67ce92da4aec485265873614|72468d8405cc3d50a48cddf595cc105b0bc87eefe39d9954ea5cf696ca398d1f|a5c7ff75686e1667498f341a054e64d354e52b9af5af0f700f541102f23cff04|f3f99c37cd2c749df8946b61796dfd282fe8a4fd3442840907205b7096c00226 +case_1|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233|0|20e1e5aabb0dad6233ace009e984b30076861b66ccd0d667eda84548e9d49e01|9ea25c2d08110d29b835d6b223516bcc263243600f3d74f0df42669826934205|d207f0ace85e198c94f2501d9359c341f8a417392b44227102be5cb480341f0e|f7df70fb9e26fb89da351a8631e20140b05af2869387e8256bf07a78bec0e41c +case_2|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681|0|e09f95cb89496d88e2803f475675110764a4555663ca26756ca1716435b25707|ed1fff4c701ececfdf36854158fb29d5bdecc4d990109383a7a1fcd1e5e5e712|7621252f156ca6c42853e995d9a21047ad00f9fe033633b95542be0516e37a21|54fc08da1ace23f287b6635f0525230c7acb93d16d3c26df3f1155a519aea112 +case_3|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad|0|f44d9be6ea3cde876795216f77d0b6235375d3c3e80b0a0a6b95f51b3ad03929|8f9f5db4a1e614c70117ebf24155362cf19a12a4180082fe57c7e4b062aeb62e|ff075835b61effbdb402a390c73dc401a226c5cfe6df75443015d2b06b5b162d|ef9beb63573c6e9b8c50d476b0d669b35896ccbec5c6b0301fc0e1373a3f5c0d +case_4|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad,14a266f67248f8b0281624f8624aee9e2e46f36a706def54e190a8324d0aa9dd,0b6f98682de3c12e476e4c08ecadc16898af8f98e42045c6cab0b33b9566d3c0,1080d264fe1d7715509c1583f080f903b0d9df6d29963f9f6b20a5cd8cc65199,24366f8705222747ce56c85b18282e2fd68a6e4f2a50087c471b556068d10b5f,29cbc5b641430a2036672aa0626bca66e71e6aad7ba6758abf4271df73635fdc,30001a7578c0efcc23a4de0a082794619dcfaa78071d54e0cdc684c4c916562c,14914203a175a4b0ade0119b23188c58a97f8dc4dfe2414e86519d812ede0303,024ed1dca7f18ca67a9370f93db66c08117503d86426ad3fc973d61d6d3bd49d|0|aaddb4bee20e54b46e024bc1362a3f06a7b5166069d6c74de64285fe5ab5942a|2d29f435697f30ccd1da5923471ad7638da8161a5a2d526b08031628a770ac1a|cf03ab3e35d3ff0de7bd3eb6727ab12df91b69acc74d3be5c5e4d3e91b8dc91e|6c8c6ed442099e3e6d8fc13d7966dc266c9faa7a477c15734274b7a59f36f52d +case_5|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad,14a266f67248f8b0281624f8624aee9e2e46f36a706def54e190a8324d0aa9dd,0b6f98682de3c12e476e4c08ecadc16898af8f98e42045c6cab0b33b9566d3c0,1080d264fe1d7715509c1583f080f903b0d9df6d29963f9f6b20a5cd8cc65199,24366f8705222747ce56c85b18282e2fd68a6e4f2a50087c471b556068d10b5f,29cbc5b641430a2036672aa0626bca66e71e6aad7ba6758abf4271df73635fdc,30001a7578c0efcc23a4de0a082794619dcfaa78071d54e0cdc684c4c916562c,14914203a175a4b0ade0119b23188c58a97f8dc4dfe2414e86519d812ede0303,024ed1dca7f18ca67a9370f93db66c08117503d86426ad3fc973d61d6d3bd49d,19f45e6d718d1b7e93fbf397dbe47daf1f5ec9ffdd705392e2ebbef8d0bb012a,282b1ccd8823e7b3e62d031ce11216347af5a68b5f0219f1c4aa2bb136413030,22af2f34e42e134b50119a8e69317706740301cb0992daef63f37a320675f9cc,2b3449c0e661dfc71e491002cbe828e3fb746777a80c8417c11d3c5664da3779,045da2e85c941fbcc9a5ec4e39d1664a50a417afb81e3ecf6ac98e883f73e718,1694c52c456771424433f4e6456223edb91863e9ed4fc177cd9708d74f0c05a4,01a2c7ebf5ac55c1134145d5a25879957f77ad7c5ff5d830e95d2cded1bff837,306062fa2e55d95eace092c61b1861ac70a0d9f444e98095433da2cf4af62c5b,10c195f1c6456ef6bc62eed98ed8d2d8c2a2a34cd6f6a38dc01b0fa3adc7d798,2bf01d26d6d9d962e39e0de82dc29a7ae54e2410497d04da3f92ade75d8ac210,11f3d4c0934a22bf13f1876c94de60e2ed365e58694f236462d9cbca352ed1d1,06a1506db653b2c73039fb3cc66ede2f542c97b362d0bc64b760bf27cdbe5e45,1c2a8378fd0cf16d88222c2ef8631876c6a744771b074cc4d93abeba98f79160,00ea34834a54c8f56b9033449b1b85684942bcb4ee7cd5e5d4ccaa5c24a31915,2a5b5653089f72b0c45e027c89a32b02058dbdcc8011af46f4013da582ba9bc3,080186e3162327954e0443677e99dd7974326f4759c98b309813221d8fb59834|0|5f8a6aec174628feaa7ab6c02cf3c5af57b09be4db81bf0bb1435d703168b623|b8e4bb9e04471b022f925db89d32a183e07e2efe0a45e2cfc19c10399bf7b515|f31eaf6d94eefdb902b6ac0d4a1d6cc3c271504f280483dacac484f131b0f218|531c847a97bf493b68f149d1d42ad0bdd03e5c956733d16dc9e73d7766b3c819 diff --git a/packages/zolt-arith/src/testdata/pairing/generator_vectors.txt b/packages/zolt-arith/src/testdata/pairing/generator_vectors.txt new file mode 100644 index 00000000..cdf8807a --- /dev/null +++ b/packages/zolt-arith/src/testdata/pairing/generator_vectors.txt @@ -0,0 +1,8 @@ +# Pairing vectors: e(g1_scalar * G1, g2_scalar * G2) -> Fp12 (LE hex). +# Identity cases (scalar 0) produce Fp12::one. +# name|g1_scalar_u64|g2_scalar_u64|expected_fp12_le_hex +case_0|0|1|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +case_1|1|0|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +case_2|1|1|950e879d73631f5eb5788589eb5f7ef8d63e0a28de1ba00dfe4ca9ed3f252b264a8afb8eb4349db466ed1809ea4d7c39bdab7938821f1b0a00a295c72c2de002e01dbdfd0254134efcb1ec877395d25f937719b344adb1a58d129be2d6f2a9132b16a16e8ab030b130e69c69bd20b4c45986e6744a98314b5c1a0f50faa90b04dbaf9ef8aeeee3f50be31c210b598f4752f073987f9d35be8f6770d83f2ffc0af0d18dd9d2dbcdf943825acc12a7a9ddca45e629d962c6bd64908c3930a5541cfe2924dcc5580d5cef7a4bfdec90a91b59926f850d4a7923c01a5a5dbf0f5c094a2b9fb9d415820fa6b40c59bb9eade9c953407b0fc11da350a9d872cad6d3142974ca385854afdf5f583c04231adc5957c8914b6b20dc89660ed7c3bbe7c01d972be2d53ecdb27a1bcc16ac610db95aa7d237c8ff55a898cb88645a0e32530b23d7ebf5dafdd79b0f9c2ac4ba07ce18d3d16cf36e47916c4cae5d08d3afa813972c769e8514533e380c9443b3e1ee5c96fa3a0a73f301b626454721527bf900 +case_3|2|3|a28d573b72cd18dd69d75fbde33122a827173dfee47992577b6ed860494d170d9a2cf587efa38f27a883a6ff6c585502aa2cc2b1b2ceb6c96c37045c76f2750318324928b7b0f825bc8841c06fddd134ed7a6f55f015dc2d8bc9ed41bbc4d10885d103ceedc73bcdb62ed90bfeb0a9b8d0be69530cfc0cfebbaae66eb755a714a3451756a6ff6659adf985eb42859415734974e59141ad9c9092af422fc2c011406163bab4291022908359e7de047c82678bf85559dd535ecd7ac87ccb2f56238565cc8dfab75c7e1f0393196409f075fa3eeefc0c565db58bcdb5cc8438e625e7ab34cbcd5be256c170b4fd5fe2a3dd2559f95acb9a0c00a3b147da6084490c1a845e74252f1443f52259087865c9219262d5ebd1a171038242bd30c1b1d52511c7068478d244fd651d68bc1bb3fd2c47ae43a313cf3041155413725fe62325902530eb1585505967a8044427bc1ace45092bd88f04e26665d358af05f0092658da3c5cdb2da1bfdd4bdeb12640b28f7f23b339a5084cfcd597ace6a39bbe2d +case_4|5|7|2e8d72665ad5bb89cf47012e28901b978ffd931b8f7d0f242b8091381e42af00ebbd572ead64f291e6b52090beb6d3886f502e99292b30f2e8633c64f2b6bd217f0d9e2374bb2c64245795237d5516281a359f39a33757b60122c1c6be1286144ea1f8fc07c163b3dedd5b154d16769aebb60c51a81888373bfee2433f172f1fb3ee6663e6511a91df0282031c74b315b2172b6b124d616062072904e095432a378812220e033501364dd69646c13551bbd801b63568f7c01c5d3db8cae9ff2cf5dff9e8f24bbfd3b3e56d51441bab94e840b93ce6ed943556a3515047dd982faea0f8377b9bf5a1a8cf209c68632517df6500d6660384f68a21cecd8602c20b2f39502ebea2ef018c8de57d045e59e338870c12d2cc962778a40dec25bb1f172a84a5807fd2c69ef76d430d747f08b448b7fadfb94705607b07518495a3cd0dbe2d6d64bd4723523faf48da176090fa5a207f1e5f76acd24be46be790f2b2102be9928ee2f75c881c74649586bb6b0bdd4d67bd2d82ec32cc2962111e167b12 diff --git a/packages/zolt-arith/src/testdata/point_compression/g1_vectors.txt b/packages/zolt-arith/src/testdata/point_compression/g1_vectors.txt new file mode 100644 index 00000000..04be405d --- /dev/null +++ b/packages/zolt-arith/src/testdata/point_compression/g1_vectors.txt @@ -0,0 +1,9 @@ +# name|uncompressed_x_le|uncompressed_y_le|compressed_hex +identity|||0000000000000000000000000000000000000000000000000000000000000040 +generator|0100000000000000000000000000000000000000000000000000000000000000|0200000000000000000000000000000000000000000000000000000000000000|0100000000000000000000000000000000000000000000000000000000000000 +double|d3cf876dc108c2d3a81c8716a91678d9851518685b04859b021a132ee7440603|c4a2185a7abf3effc78f53e349a4a6680a9caeb2965f84e7927c0a0e8c73ed15|d3cf876dc108c2d3a81c8716a91678d9851518685b04859b021a132ee7440603 +scalar_42|343f9b2f88177d5317c5e46d58775535fbf727aefd9a8f7cd71f97b65df38809|03b24c3b2cdb2cd50599f495aea8b46028d56d6e0a390770c6c8af3fa6ffba23|343f9b2f88177d5317c5e46d58775535fbf727aefd9a8f7cd71f97b65df38889 +random_0|85d5302458b3bf320d67ec67a4d2c2e8616b9af5c732e873db7da333e98ae825|5900c9762be113984fba18f842ab80323bef3feec8a2159d60391230a492e42c|85d5302458b3bf320d67ec67a4d2c2e8616b9af5c732e873db7da333e98ae8a5 +random_1|b670e60828df3ea8bad9d4469de088178a0304f4551fc24b80993aef0048c513|3c807872da768bc5dc063cbf2c2fd8cc70e25c538bc293d791b1004407349516|b670e60828df3ea8bad9d4469de088178a0304f4551fc24b80993aef0048c513 +random_2|8810f677d97bcfba5cd8ff481010115cf22e11024fe21afbfe7a5ca00a3f3b2f|ea41281340a0d59c09c6ff9a2c1df929838aeb86061a2707069a362dde195613|8810f677d97bcfba5cd8ff481010115cf22e11024fe21afbfe7a5ca00a3f3b2f +random_3|e2b172dcd926cb8e3241c599c234828766c253a1b5a271dc9b088799bf6e2728|269dca0e9d72f23ced0a58eef880ca3439651286e68876b740140fc8e7557f21|e2b172dcd926cb8e3241c599c234828766c253a1b5a271dc9b088799bf6e27a8 diff --git a/packages/zolt-arith/src/testdata/point_compression/g2_vectors.txt b/packages/zolt-arith/src/testdata/point_compression/g2_vectors.txt new file mode 100644 index 00000000..a0155c86 --- /dev/null +++ b/packages/zolt-arith/src/testdata/point_compression/g2_vectors.txt @@ -0,0 +1,9 @@ +# name|x_c0_le|x_c1_le|y_c0_le|y_c1_le|compressed_hex +identity|||||00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040 +generator|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018|c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19|aa7dfa6601cce64c7bd3430c69e7d1e38f40cb8d8071ab4aeb6d8cdba55ec812|5b9722d1dcdaac55f38eb37033314bbc95330c69ad999eec75f05f58d0890609|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19 +double|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc2779ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203ea0 +scalar_42|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11|919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097412|72c1c48de52409d357618aac72fcbe14cf59f05162b5449630b6772e04416407|db41a40b7cd0c6a84e832bd28bc179c9c5ac040dd0ed7d4a6bf8e516982d2225|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097492 +random_0|514b3b5ec84dacbb8b474c3f22adf9ad301c664f281dd02c99babed09920d40e|303908414ff32aac7289eb655ba7ba9ea31f489e912e734e51005da548b5b112|b35cf96fb5a9459089d446ac0d6d83ce6a87ad617d8ce7c5d82b43bcb69c1910|78152fa1d65c5787a8b876888cac1b30188da6d75581b870b21005753c9cc917|514b3b5ec84dacbb8b474c3f22adf9ad301c664f281dd02c99babed09920d40e303908414ff32aac7289eb655ba7ba9ea31f489e912e734e51005da548b5b112 +random_1|53de77c9866fcded2ea83c159ae4c071a6d5722e5d5919e1b3bcbb0f03e84a2f|9352d823aed9d26e3393665ea933dbe2e5d3a08de46802d12f0c14457a3cae04|600487e508891030b82144cd7d2d08f4f9d43b1a005ed8ca8c568aeb6dd9a927|ef8f7b174a15d83bb0769ef48558a2f7847f7abcdf44a7e2a02daab6670ca602|53de77c9866fcded2ea83c159ae4c071a6d5722e5d5919e1b3bcbb0f03e84a2f9352d823aed9d26e3393665ea933dbe2e5d3a08de46802d12f0c14457a3cae04 +random_2|8dfe9801a847767875b5ba27e0b768b96f377e9ecb26a41aa451136341c5cc2e|728dc039a18cf399845c61e75e784db5173f670f7089fad7d81c7b228c5af00e|1e8915ca3c172976a9476090db85b55aa00af6130b3d3774122e6c5a7587f025|e0fa844a436efeaf85702bb4602abfd919784bcf839725f5e06b144627e2210f|8dfe9801a847767875b5ba27e0b768b96f377e9ecb26a41aa451136341c5cc2e728dc039a18cf399845c61e75e784db5173f670f7089fad7d81c7b228c5af00e +random_3|c3f838751f55e293a0e9a7846c8c28b38f6eaa571ed044c5bec43834c5b6a00e|7f25461c368b50e773c17333355699748933ce7a0e136ca3fe321cb9e1503324|d15d1c8a2465d0b0c7efde8ad97ddab37505055c72eb2df11b09e9618874e517|eb3c60c59d91e575a707d3fcc82d0e54c23a55da52af473b08743db1d152501e|c3f838751f55e293a0e9a7846c8c28b38f6eaa571ed044c5bec43834c5b6a00e7f25461c368b50e773c17333355699748933ce7a0e136ca3fe321cb9e15033a4 diff --git a/packages/zolt-arith/src/testdata/transcripts/blake2b_jolt_challenge_vectors.txt b/packages/zolt-arith/src/testdata/transcripts/blake2b_jolt_challenge_vectors.txt new file mode 100644 index 00000000..a0c92859 --- /dev/null +++ b/packages/zolt-arith/src/testdata/transcripts/blake2b_jolt_challenge_vectors.txt @@ -0,0 +1,3 @@ +# name|init_label|append_label|expected_u128|expected_low_hex|expected_high_hex +challenge_u128|u128_test|data|112132316132180403369405744574678933239|-|- +challenge_limbs|zolt_compat_test|test_data|-|5207e9d28bcca994|0737345c88127af8 diff --git a/packages/zolt-arith/src/testdata/transcripts/blake2b_jolt_state_vectors.txt b/packages/zolt-arith/src/testdata/transcripts/blake2b_jolt_state_vectors.txt new file mode 100644 index 00000000..39c51614 --- /dev/null +++ b/packages/zolt-arith/src/testdata/transcripts/blake2b_jolt_state_vectors.txt @@ -0,0 +1,6 @@ +# name|init_label|op_kind|op_arg|expected_state_hex|expected_rounds +initial_state|init_test|init|-|40f6e61cb828bcfbd5143a3df302021d6ab7f85dad9d083b274537ba1e7391cd|0 +empty_initial_state||init|-|89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3|0 +raw_append_label|zolt_test|raw_label|hello|3a2586879f2c7b911e0f006e6c3cc9ee03507c28b818138283508b192d43168c|1 +raw_append_bytes||raw_bytes_hex|010203|d5231a0c53bf96ca563e5d13ae110901fbe806e22f0ebb52c70fedf78fb26f82|1 +raw_append_scalar|scalar_test|raw_scalar_u64|42|e65e778c5261da8b62cb738cbc54033ec433d0cbe73ef37dad1337c060bead31|1 diff --git a/packages/zolt-arith/src/testdata/transcripts/blake2b_rfc7693.txt b/packages/zolt-arith/src/testdata/transcripts/blake2b_rfc7693.txt new file mode 100644 index 00000000..c78bf1d0 --- /dev/null +++ b/packages/zolt-arith/src/testdata/transcripts/blake2b_rfc7693.txt @@ -0,0 +1,4 @@ +# name|input_hex|digest_hex +empty||0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8 +abc|616263|bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319 +range_0_255|000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff|39a7eb9fedc19aabc83425c6755dd90e6f9d0c804964a1f4aaeea3b9fb599835 diff --git a/packages/zolt-arith/src/transcripts/blake2b.zig b/packages/zolt-arith/src/transcripts/blake2b.zig index 842506b7..ffd4e494 100644 --- a/packages/zolt-arith/src/transcripts/blake2b.zig +++ b/packages/zolt-arith/src/transcripts/blake2b.zig @@ -13,6 +13,7 @@ //! - Challenges are 128-bit (16 bytes) const std = @import("std"); +const testdata = @import("../testdata.zig"); // Debug output control - set to true to enable verbose debug prints const debug_verbose = false; @@ -350,6 +351,88 @@ pub fn Blake2bTranscript(comptime F: type) type { const testing = std.testing; const BN254Scalar = @import("../field/mod.zig").BN254Scalar; +test "blake2b rfc7693 vectors" { + const fixture_text = @embedFile("../testdata/transcripts/blake2b_rfc7693.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(3, line, '|'); + + var input_buf: [256]u8 = undefined; + const input = try testdata.parseHexIntoBuffer(fields[1], &input_buf); + const expected_digest = try testdata.parseHexBytesExact(32, fields[2]); + + var hasher = Blake2b256.init(.{}); + hasher.update(input); + + var actual_digest: [32]u8 = undefined; + hasher.final(&actual_digest); + try testing.expectEqualSlices(u8, &expected_digest, &actual_digest); + } +} + +test "jolt compatibility: fixture-backed state vectors" { + const Transcript = Blake2bTranscript(BN254Scalar); + const fixture_text = @embedFile("../testdata/transcripts/blake2b_jolt_state_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(6, line, '|'); + + var transcript = Transcript.init(fields[1]); + const op_kind = fields[2]; + const op_arg = fields[3]; + + if (mem.eql(u8, op_kind, "init")) { + // No-op: expected state is the initialized transcript state. + } else if (mem.eql(u8, op_kind, "raw_label")) { + transcript.rawAppendLabel(op_arg); + } else if (mem.eql(u8, op_kind, "raw_bytes_hex")) { + var bytes_buf: [256]u8 = undefined; + const bytes = try testdata.parseHexIntoBuffer(op_arg, &bytes_buf); + transcript.rawAppendBytes(bytes); + } else if (mem.eql(u8, op_kind, "raw_scalar_u64")) { + transcript.rawAppendScalar(BN254Scalar.fromU64(try testdata.parseDecimal(u64, op_arg))); + } else { + return error.UnknownTranscriptVectorOperation; + } + + const expected_state = try testdata.parseHexBytesExact(32, fields[4]); + try testing.expectEqualSlices(u8, &expected_state, &transcript.state); + try testing.expectEqual(try testdata.parseDecimal(u32, fields[5]), transcript.n_rounds); + } +} + +test "jolt compatibility: fixture-backed challenge vectors" { + const Transcript = Blake2bTranscript(BN254Scalar); + const fixture_text = @embedFile("../testdata/transcripts/blake2b_jolt_challenge_vectors.txt"); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = testdata.cleanLine(raw_line) orelse continue; + const fields = try testdata.splitFieldsExact(6, line, '|'); + + if (!mem.eql(u8, fields[3], "-")) { + var transcript_u128 = Transcript.init(fields[1]); + transcript_u128.rawAppendLabel(fields[2]); + try testing.expectEqual(try testdata.parseDecimal(u128, fields[3]), transcript_u128.challengeU128()); + } + + if (!mem.eql(u8, fields[4], "-") and !mem.eql(u8, fields[5], "-")) { + var transcript_limbs = Transcript.init(fields[1]); + transcript_limbs.rawAppendLabel(fields[2]); + const challenge = transcript_limbs.challengeScalar128Bits(); + + try testing.expectEqual(@as(u64, 0), challenge.limbs[0]); + try testing.expectEqual(@as(u64, 0), challenge.limbs[1]); + try testing.expectEqual(try std.fmt.parseInt(u64, fields[4], 16), challenge.limbs[2]); + try testing.expectEqual(try std.fmt.parseInt(u64, fields[5], 16), challenge.limbs[3]); + } + } +} + test "blake2b transcript: basic initialization" { const Transcript = Blake2bTranscript(BN254Scalar); const t = Transcript.init("test_label"); @@ -495,10 +578,10 @@ test "jolt compatibility: rawAppendLabel matches old appendMessage" { t.rawAppendLabel("hello"); const expected_state = [_]u8{ - 0x04, 0x5d, 0xb7, 0x95, 0xb0, 0x5d, 0x42, 0xb5, - 0xc7, 0x9d, 0x6d, 0xbb, 0xf2, 0x0c, 0xbe, 0x09, - 0x26, 0x36, 0xdf, 0x45, 0xbb, 0x1c, 0x80, 0xf2, - 0xa4, 0xbe, 0x9b, 0x66, 0x4b, 0xad, 0x5e, 0x0d, + 0x3a, 0x25, 0x86, 0x87, 0x9f, 0x2c, 0x7b, 0x91, + 0x1e, 0x0f, 0x00, 0x6e, 0x6c, 0x3c, 0xc9, 0xee, + 0x03, 0x50, 0x7c, 0x28, 0xb8, 0x18, 0x13, 0x82, + 0x83, 0x50, 0x8b, 0x19, 0x2d, 0x43, 0x16, 0x8c, }; try testing.expectEqualSlices(u8, &expected_state, &t.state); @@ -538,10 +621,10 @@ test "jolt compatibility: rawAppendBytes" { t.rawAppendBytes(&[_]u8{ 0x01, 0x02, 0x03 }); const expected_after_bytes = [_]u8{ - 0xc4, 0x21, 0x29, 0xc2, 0x59, 0x57, 0x65, 0x9c, - 0xf7, 0x63, 0x38, 0xf5, 0xd2, 0xcb, 0xad, 0xd9, - 0x5d, 0x1b, 0xf5, 0xd3, 0x57, 0xfc, 0xf9, 0xa1, - 0xe9, 0x62, 0xc3, 0xc6, 0xb5, 0xed, 0x37, 0x27, + 0xd5, 0x23, 0x1a, 0x0c, 0x53, 0xbf, 0x96, 0xca, + 0x56, 0x3e, 0x5d, 0x13, 0xae, 0x11, 0x09, 0x01, + 0xfb, 0xe8, 0x06, 0xe2, 0x2f, 0x0e, 0xbb, 0x52, + 0xc7, 0x0f, 0xed, 0xf7, 0x8f, 0xb2, 0x6f, 0x82, }; try testing.expectEqualSlices(u8, &expected_after_bytes, &t.state); @@ -569,10 +652,10 @@ test "jolt compatibility: rawAppendU64 and rawAppendLabel" { t.rawAppendU64(12345); const expected_state = [_]u8{ - 0x14, 0x1b, 0xf2, 0x3f, 0x43, 0x6f, 0x74, 0x1b, - 0x0f, 0x9d, 0x78, 0x0f, 0xac, 0x3e, 0x62, 0x93, - 0x62, 0x74, 0x78, 0x7c, 0xde, 0x4f, 0x59, 0x55, - 0x73, 0xa5, 0x32, 0x6c, 0x5a, 0x75, 0x5d, 0x85, + 0x71, 0xbe, 0x51, 0xcf, 0x72, 0x3b, 0xed, 0x98, + 0xc6, 0x6e, 0x2f, 0x6f, 0x85, 0xe4, 0x05, 0xf8, + 0x65, 0x69, 0x4e, 0xa8, 0x17, 0x5a, 0x74, 0x97, + 0xb4, 0x7b, 0xbe, 0x0e, 0x99, 0x50, 0x94, 0xc5, }; try testing.expectEqualSlices(u8, &expected_state, &t.state); @@ -587,10 +670,10 @@ test "jolt compatibility: rawAppendScalar" { t.rawAppendScalar(scalar); const expected_state = [_]u8{ - 0x0d, 0x8c, 0x5a, 0x29, 0x4a, 0x38, 0x74, 0x89, - 0x89, 0xe3, 0x60, 0x61, 0x7d, 0x26, 0x1a, 0x04, - 0x73, 0x5a, 0x30, 0x54, 0xff, 0xf0, 0xf2, 0x9c, - 0xa3, 0x6c, 0x9d, 0x32, 0x28, 0x4e, 0x3a, 0x7c, + 0xe6, 0x5e, 0x77, 0x8c, 0x52, 0x61, 0xda, 0x8b, + 0x62, 0xcb, 0x73, 0x8c, 0xbc, 0x54, 0x03, 0x3e, + 0xc4, 0x33, 0xd0, 0xcb, 0xe7, 0x3e, 0xf3, 0x7d, + 0xad, 0x13, 0x37, 0xc0, 0x60, 0xbe, 0xad, 0x31, }; try testing.expectEqualSlices(u8, &expected_state, &t.state); diff --git a/packages/zolt-arith/src/transcripts/mod.zig b/packages/zolt-arith/src/transcripts/mod.zig index f8a3bf93..240b7b3c 100644 --- a/packages/zolt-arith/src/transcripts/mod.zig +++ b/packages/zolt-arith/src/transcripts/mod.zig @@ -375,6 +375,10 @@ pub fn PoseidonTranscript(comptime F: type) type { }; } +test { + _ = @import("blake2b.zig"); +} + test "transcript basic" { const F = @import("../field/mod.zig").BN254Scalar; const allocator = std.testing.allocator; diff --git a/src/zkvm/spartan/stage5_lookups.zig b/src/zkvm/spartan/stage5_lookups.zig index a01532c8..60651a30 100644 --- a/src/zkvm/spartan/stage5_lookups.zig +++ b/src/zkvm/spartan/stage5_lookups.zig @@ -1092,7 +1092,6 @@ pub fn LookupsReadRafProver(comptime F: type) type { .raf_flag = computed_raf_flag, }; } - }; } diff --git a/src/zkvm/spartan/stage5_prover.zig b/src/zkvm/spartan/stage5_prover.zig index 3a1fc181..67e2f833 100644 --- a/src/zkvm/spartan/stage5_prover.zig +++ b/src/zkvm/spartan/stage5_prover.zig @@ -1642,8 +1642,6 @@ pub fn Stage5BatchedProver(comptime F: type) type { init_sub_timer.reset(); } - - // Run the batched sumcheck if (comptime debug_verbose) { dbg("[STAGE5] Entering main sumcheck loop, max_num_rounds={}\n", .{max_num_rounds}); diff --git a/src/zkvm/spartan/stage6_prover.zig b/src/zkvm/spartan/stage6_prover.zig index 84faa4f4..7a9a30d9 100644 --- a/src/zkvm/spartan/stage6_prover.zig +++ b/src/zkvm/spartan/stage6_prover.zig @@ -1683,7 +1683,6 @@ pub fn Stage6BatchedProver(comptime F: type) type { .allocator = self.allocator, }; } - }; } diff --git a/testdata/zolt-arith-diff/accumulator/batch_inverse.txt b/testdata/zolt-arith-diff/accumulator/batch_inverse.txt new file mode 100644 index 00000000..91dd907d --- /dev/null +++ b/testdata/zolt-arith-diff/accumulator/batch_inverse.txt @@ -0,0 +1,6 @@ +# name|count|inputs_be_hex_csv|expected_be_hex_csv +case_0|1|289b304a3fc967b677ac63c8dcdbe7f94eab2e7ac84bdd3ceea205d59927f1bb|17e25c79cd8dec5fbf409b35e6452a0f0ddd361bd83c3ce12a278d193c119f5b +case_1|2|05cfeab23019661a0684149c9913ff28f06fbe69048f94f93c4636ae38d82dfd,248f75d6b4d0db7df15272afb437db14fcd616a2a78f3c45520dd2059fed51a1|0a0a9af84e95c467dc050e0161194cfa8cdcedcfb1d80bbc402c3789c1585d56,227c84f0e56033658b937404c0dba7acae82bccd32a4487a9a06c1b45e7da136 +case_2|4|29bd723d4fd19fd01733f0b2ba7d7eafebb2a01f750befde92c167b7bec62b65,002a6b8567576544c921864d4829121100309d1b27e7deb49926933a620d386f,1957da32d98083d710d1b79ca293efcc70884666aa41e5a845d0b15ce97e3b91,0df88e899fcf4d7c33479e99ca4a868a2db07393ad9ac61554b48787d4076254|0b0c369b8271f8509967b00de3480f04a1efd761d0cd268ec4464723b23c5d0a,20ed3264701787eb8f232f7024788d611277e40477f0ba08c883f530e2ebe92b,25c372a88c926b562bb390be5771ee3d26fb6115e74416b714ca4a621c8b0863,06b79d6d425626b7c108bd8c853966c92c893a95a9a23ad36b62415d37b07f5a +case_3|8|12c912c66eb7cbadc03e68573671cf8db285d0b2d0b168c6bfc746ddd2fbf89b,2adbb5b1a9e547ef653b0c3ba0bfafd3cdd71c1c56fc188d2baf83783e7b03aa,02f0562f676635ce764ed6386563e05de8acec7b9c581c3904a2f62c5b0d4b78,25f797822d274b64b9a4a51acd79f820754fefeb407fd54e2237123140a060b3,11d244995a85044804727213fb46b59019d5aa658fdffb873ab7289751d16b66,29de201504b5ca6a520d3862e67ddf300b500d34285fa428621538f7026b9310,0fadf41129140f9ed3e8868db570497569630b23a55284bbe122df953acd358a,0b902f5d2d9cf6b58eec65b59aea8f3ce0a4e9b8cf2aa7be08a5476f7a2c57b0|189ccb40ac2cc04879f564a6065c247002a4ad5f5eb97cf24cfc6bcc7d00b1b3,2925c6c301dceabd5aaade9c2dab87f42e9c3e11c7e4ed4c9ad28bcd1b093a5e,2d2e35406bdb03693bea000eb08524760f66172908dbe559f8242ba0bb90671f,1190be17922cc089192684cba8e6b8beafdcab1b9decfebfffe97ef6ee85b60e,09789037e3675d38757f62e813a16e069aea3efa9146ad97e7a80f32b64ea66d,22725c09c28afd1ed82d703bcb84175e6f10aa32c0f1b6345b19ac9b4589de94,248456e00e04fb8ea471aefd2a45120039d54700b79608fe38c0a250df489073,2549355e71f9d913cf5e51a88abe3e68d858eb88cc5b4e931dde0dda8aa7c20e +case_with_zero|3|23a6275ca0db64c463d2cca0f2ab27dbcbfbceb7da595fc60e6ed43f2345117e,0000000000000000000000000000000000000000000000000000000000000000,2b6c400c03b0685719b47b101c6cae72d0452d2364eedcbc2bff17fb9c5b319f|149f7d86c6cb81f00746bc30c772f45242a77d55a46507ff31b069de544ea404,0000000000000000000000000000000000000000000000000000000000000000,12935110a19466ad5203814032ff6b2ba5c464266b900e6f11371198f02c7cbf diff --git a/testdata/zolt-arith-diff/accumulator/mul_u128.txt b/testdata/zolt-arith-diff/accumulator/mul_u128.txt new file mode 100644 index 00000000..4cfb92fb --- /dev/null +++ b/testdata/zolt-arith-diff/accumulator/mul_u128.txt @@ -0,0 +1,7 @@ +# name|field_be_hex|scalar_u128|expected_be_hex +case_0|01f8c6c4ac00ab3ff80a733d710dcee5fe0e36792e19805e914c3c38f43e03d6|0|0000000000000000000000000000000000000000000000000000000000000000 +case_1|0eca9adaeb9a5ffe66d9e57cea0fbc9be1fe8a012188ac18f2533b0dd7fa6443|1|0eca9adaeb9a5ffe66d9e57cea0fbc9be1fe8a012188ac18f2533b0dd7fa6443 +case_2|08802f6a6fff49c0b22d7bc9186d82bfb43703ffe796889523856557cd35204f|18446744073709551615|09e88388ed95f9a065b3dae3b372ec4686da86a25fa40ba05085e57fd6bc9650 +case_3|10c49083567067b78a0e76d9e0c394e1313acb09ebc4d3a2ee2f6b35929fda9a|18446744073709551616|0a67e3df0225a8b1af6feabb0674b27170610a603c561b4350760b02c55dc5a4 +case_4|2e343ed4306cbddca6a787bc1fbd581b249eb679f13bbf0fc7985e19901de6ff|295990755076957304699390954001009532656|07ee289b77592e2292b8367caf9cbf3b393f87038fc7488e3163f8fbbf8ceb19 +case_5|2528995b02d0eebd542d72bd69fbbbc4d2889e93d936e8edbce5f0d806d1e8e5|340282366920938463463374607431768211455|0486a28f94f2bb14d3a591e62425b0d4a206beb4aa17d8f05391391182a44510 diff --git a/testdata/zolt-arith-diff/accumulator/mul_u64.txt b/testdata/zolt-arith-diff/accumulator/mul_u64.txt new file mode 100644 index 00000000..c5cde6c4 --- /dev/null +++ b/testdata/zolt-arith-diff/accumulator/mul_u64.txt @@ -0,0 +1,7 @@ +# name|field_be_hex|scalar_u64|expected_be_hex +case_0|1c33d4d664300de7310814095ab95bf0017128faa3e4494dc1caf3a81be5eba0|0|0000000000000000000000000000000000000000000000000000000000000000 +case_1|19d5dad02f20d4406e7b58839c77b298db2b60ee375d23cf572e04999aea930f|1|19d5dad02f20d4406e7b58839c77b298db2b60ee375d23cf572e04999aea930f +case_2|08f3ba094c89579c10edf6f345fe7bf950e308e1dc46f02dbc90af922c656bb5|2|11e774129912af3821dbede68bfcf7f2a1c611c3b88de05b79215f2458cad76a +case_3|2b568ef164789b9bf136b4dc118e87f3cfef36faa92ac39068743cf7a7e1115d|12345|257b63ab4e047954a4a5140821a31d6b0d066bc7303c8daaa2941325845e2286 +case_4|12ae82acf1d21127be1f660f245162fc163963cd4ef2d52fd0adf1e8b3e83aae|16045690984503098046|1bb191ca238e65862a367b7675e5bc05a3bcc2becf4d3e2e06c287f8380c056d +case_5|222b4cca874d39189e31eab331ba59d57f253010c83911801a6101b61adc544e|18446744073709551615|0559ef6e8472cdf4d630293e5bce66bea32e1a437e84d7244781776adb2b9dbc diff --git a/testdata/zolt-arith-diff/accumulator/sum_of_products.txt b/testdata/zolt-arith-diff/accumulator/sum_of_products.txt new file mode 100644 index 00000000..ae243958 --- /dev/null +++ b/testdata/zolt-arith-diff/accumulator/sum_of_products.txt @@ -0,0 +1,7 @@ +# name|a0_be_hex|b0_be_hex|a1_be_hex|b1_be_hex|expected_be_hex +case_0|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000000 +case_1|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000002 +case_2|0000000000000000000000000000000000000000000000000000000000000003|0000000000000000000000000000000000000000000000000000000000000005|0000000000000000000000000000000000000000000000000000000000000007|000000000000000000000000000000000000000000000000000000000000000b|000000000000000000000000000000000000000000000000000000000000005c +case_3|00535b805b9b5ea6ea71d53e623128a4f6d9f9b68b56fc7506b5fa825e25d63d|150c8897491dd80acead95fd3743a78eacad875b1b3e23708f1871a7eb9310dc|231f7ae988953cdbd60396c0018f031019c880082626f59594394fb0c1617e13|11c707592081e8afb799b075b49ccc767f38553e8c6b9f72a973eee9542bcef4|270ebe3da8a4ede7f8b7c1cab387408df1911e0f090d7a3d5e76e72445b99448 +case_4|1a52f01094b7bbc215cda396b952c7d04b42bb0d983ca53d6c31d42a576a4319|25758b2b22490bac92ef8f590b6cdb254f4bfb485eb5839261f88c0c32ae807d|150c78bc17dcdc0cb3c59cbe4b085a4c3932791bb598c7b7d3814fab81e3a6a0|0c1627560a07b24444b81d4d4826740aac65af3e8af4c1808d47c18524652242|0e6062dfaabcbc8ffa0b22dcb219d2f97e5d3ba771c1c5f8dff143f366a8abf3 +case_5|02630fd54380d2cc5535f99690173be468e75b91757b24e8c171fd8f263631d5|161f353f014a3b0bc53aff71c70b21c768466d0862c7c3825093a322a7c9d58e|0c0cc63169161646ecb7bcef9fc3c4ff427c591c0d6aedf7a24a9811f3a9ecf8|0e8fdecaa8f7d4a67bf3c805e10504beddd3932441e057960d7da154c059e0aa|24f39d87a9f7792bc968cc9684e449935d337e898292f9ddd12b4cb6adf59a3a diff --git a/testdata/zolt-arith-diff/dory/commit_cases.txt b/testdata/zolt-arith-diff/dory/commit_cases.txt new file mode 100644 index 00000000..b9a528bd --- /dev/null +++ b/testdata/zolt-arith-diff/dory/commit_cases.txt @@ -0,0 +1,4 @@ +# name|max_num_vars|evals_be_csv|expected_commitment_fp12_le_hex +case_nv2|2|0000000000000000000000000000000000000000000000000000000000000001,0000000000000000000000000000000000000000000000000000000000000002,0000000000000000000000000000000000000000000000000000000000000003,0000000000000000000000000000000000000000000000000000000000000004|5e8872c053cc233d2a624655c5b38ef483677f81f93dc72454d2ee583892790f171c95196d5421258a2a91c0e91c625dc999784be4344536bac52592631bac2e191aad80c53f294348ecb97560cb19e1783ec0a3040ef75a1f2b393a2f320f2382dc1760aa54833f90c06b5133ee62c2d46d530268b81426f9103cf5c538cd2078051446b9243ac012da1e5dda27716339b8f2e885d377625ccf0ecf5c5311152fc79535cdd0f6f89d9ece132949fd4be733501540a8b4acfcbc59fa53cf9a170a93d283bc6250c66513b1cf1c1838892b60b93afc9f5198568f63aaf46b0f24b2ba2344c6957abe0d9a944847b972e08219a4ec7b0e07663180eba58b35ea02a9fb2060411ace4de9fc4e3899a62b3a98da1febbb13a50a3f05035b3ce8812f5e079e40845767fc64ace0b54d55a1062d9cf1493143b877cec55c789351d21fb6847457d9d7ce6d597dfaf4c2f5ee8c9373d331e819a7aa2de74af37d2c6a1d6cac3f5a95d991e515b876dcc2a245beaa5cc219cbf7bbe28aa9a3b025efa51a +case_nv3|3|0000000000000000000000000000000000000000000000000000000000000001,0000000000000000000000000000000000000000000000000000000000000002,0000000000000000000000000000000000000000000000000000000000000003,0000000000000000000000000000000000000000000000000000000000000004,0000000000000000000000000000000000000000000000000000000000000005,0000000000000000000000000000000000000000000000000000000000000006,0000000000000000000000000000000000000000000000000000000000000007,0000000000000000000000000000000000000000000000000000000000000008|25517613f4b4ce4d60c723366f6b3c968ba0600e0472fda8099949163537be0eb8e11c84443a3787ac0f812f3e35969a98916d9e5a8c3197d68c9ed7ec37821269cd7ed2e501f016e350e2297b8ad208cf9f9cbb2d68ff80938ed029fd77570a744dd519a27d70008ffdca453d2ae24df599209c789ea36ed49d41686547a72d6506e1d7ec7fd5e5ee7646a084bcd7ac8e5b02af7114052415c5885e8c00b20790ce49f7375b1a10a5f5c39789b0328e1a886720505fc45a32d8260c121c52251f24729788813adf9359d699d502dece78565095823c6e280c62920852db4f3090df4cc1897e603befdf50d6f6a39ec9b9dfdf18c898fd7501dedf9d6fac8705a734abb9dd2a9f5f6ebcc2b99289373e48f7d49fe449fd1ca3775c84e133292306bcb869eb35db3db25be200994a60588434a0b3c71cd7c4924b6ce8e29cb21cc3e80668a206e9adae8dcb5fffd63a96c635b28637ead9ccdb387831c070d820824e21de43109d1c551bb1393331804b005bbfbf52f98d8666ff3a43c7ac4b15 +case_nv4|4|0000000000000000000000000000000000000000000000000000000000000001,0000000000000000000000000000000000000000000000000000000000000002,0000000000000000000000000000000000000000000000000000000000000003,0000000000000000000000000000000000000000000000000000000000000004,0000000000000000000000000000000000000000000000000000000000000005,0000000000000000000000000000000000000000000000000000000000000006,0000000000000000000000000000000000000000000000000000000000000007,0000000000000000000000000000000000000000000000000000000000000008,0000000000000000000000000000000000000000000000000000000000000009,000000000000000000000000000000000000000000000000000000000000000a,000000000000000000000000000000000000000000000000000000000000000b,000000000000000000000000000000000000000000000000000000000000000c,000000000000000000000000000000000000000000000000000000000000000d,000000000000000000000000000000000000000000000000000000000000000e,000000000000000000000000000000000000000000000000000000000000000f,0000000000000000000000000000000000000000000000000000000000000010|029d54423b26fe65fed389e3d0fe380b7e9b8895b4cdc6223f1338c289fb8a23ba3644bc74c0b4d8bbd62fddc79c8a3dd34c01a3faf0f9a86199d06fc0aef00fa873ab52f2d2a18007f7c360c87e9501822ae40647e132bfd77ea5ffb62f6226fcd8996c325501c12257cedac599dccea816f2d1c1da0022994e9e42ecda020d97a13b29721355ea33384dec5bd866e003cb97ae80872bcaf32e53608a63de289a91bc58605eb2e6c7b9ba371fe033cd6efcbbf4d9759b1e74b0817189fb9b2e801816bcf227e239cb124fec33c1fd69c61fad4f46a36f8cb46457864d894b30ac83dff5202d91ffc026fb092f274bd0c3b7a712fdc44620a6065e5ea9d46311c435aca79f9428d6074242f104dfe58359fde9b9c5aa55440e115f34b14b47157df753b39bc80a492ba941b77b3c015c21072cb689fefa6a6f1ac8448547d80b406a49e202f65528b142dae502478df34386dac2980f3d5e592934a7545d8f139acfcd43fcb3520e4aac197ef64cf9f955a578d2b15b2d3eb6bebaf151b46114 diff --git a/testdata/zolt-arith-diff/extensions/fp12_ops.txt b/testdata/zolt-arith-diff/extensions/fp12_ops.txt new file mode 100644 index 00000000..5908bdef --- /dev/null +++ b/testdata/zolt-arith-diff/extensions/fp12_ops.txt @@ -0,0 +1,79 @@ +# op|a_le_hex|b_le_hex|expected_le_hex +add|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +sub|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +mul|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +square|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +conjugate|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +frobenius|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +frobenius2|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +frobenius3|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +add|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +sub|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +mul|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +square|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +inv|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +conjugate|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +frobenius|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +frobenius2|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +frobenius3|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +cyclotomic_square|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +add|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|0d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000|0e00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000 +sub|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|0d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000|3bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430 +mul|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|0d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000|9df27cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64305d340000000000000000000000000000000000000000000000000000000000007df57cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643023290000000000000000000000000000000000000000000000000000000000007df97cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430c11600000000000000000000000000000000000000000000000000000000000011f57cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430d8260000000000000000000000000000000000000000000000000000000000004df87cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430aa19000000000000000000000000000000000000000000000000000000000000b1fc7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64301405000000000000000000000000000000000000000000000000000000000000 +square|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|-|49f87cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430d11400000000000000000000000000000000000000000000000000000000000061f97cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430bf1100000000000000000000000000000000000000000000000000000000000039fb7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430850a000000000000000000000000000000000000000000000000000000000000a9f97cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430e00c000000000000000000000000000000000000000000000000000000000000edfa7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64305a09000000000000000000000000000000000000000000000000000000000000f9fc7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64306c01000000000000000000000000000000000000000000000000000000000000 +inv|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|-|6c0beb52b24e4d619f0f75340f716e2052390b707ab00615dc81c09d67265212e074b43705dd9b27a8b7965673ad1d65329fbe8623e2affb8caaefe817a9c027e60b54cbd4f9485605fb80c14aba823cb276d822f0bbba83e14987aa7f57931bea055d9ad3f5104a7709216955e5c05b82146c5afceb5e602bc35ac2c87b4c095c4afda607921bce6b246ca6726e654fd227b726cb5a186176c23d9f174856061eff84a2768ce2aa98316e572e70ddf64532be588f2597bfaea448569e0d262cb892aeec1968486666592c14f9dc1cd3c47fd8f8270e06a58bf61a036482f11560cf6e76f780b94520962743349e2a7f6caabed7bde057c6f9106abc8c679d01a53f45853b940a9979fd8e06e725fa525f247eba065657d7ee68e6b2d65c0612437b532a09168d9523413021adb86b02573dccf80e82f7a1700f5cb7ef3e5b2342771018baa4e6bdffef9d0002644122e65455050344bb62be2f9cc0ee05580f533a418176e3cfb7df9999617e91719813a67613ee304e89fd79a93b09423411 +conjugate|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|-|01000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000040fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303ffd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303efd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303dfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303cfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303bfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430 +frobenius|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000045fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430233999741be4bf3629e674d460475993a5d06f370ce07531c06d53f13e32b028c705e9573dce0490df6cbe50c8b15b80c65a2b35d385afdb00a3738be87cbd16245cb0968dfe52b5438b76edea6f1bdad331b31b6f2540eea4c870214eefaa029da1aa0b13038af69bfa86da0f7be456a5ce2286012e7ded22e6c686bc12f928386a384d419b314c04d531546a11006e2969ebc44a1925cc06d511b9cb45cb21a6945eef97cbef5f77d62acee03bbf1d27455cc499e0f3d4fbd74bede2caf4097a9c0bcb2bedf5627e2788c317fd03d97c6eb6a78088e39474929985a84dde24777600e7bb6d27fe6dbad76acda2d7984127672bf0a076a3c98e3b386da460076da37fdc8b13cdc9df0832e797defac1ecd7a361a99a3bc209924e79a27c332f300176302fed86b0b328cd359b3cb1b2dad1e95a534bd1e3191b98bf826c142a +frobenius2|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004afd7c7082b6f6359fb5df61bc3eaa1857b5039f4a02a9aa28a031e1724e64304bfd7cf8506f93de4f5904b5cadab743aa292ffe7b96c65028a031e1724e6430f6ffff57f763f0b48ccd4860b8f3bb2860ba2624091b6cc10100000000000000f4ffffcf28ab530cdc29240daa57aefd0c46fbc4d7864e1b020000000000000055fd7c90bc9969d8614472aef5aee0c4a386b11b10531f4327a031e1724e643057fd7c188b52068112e89601044beeeff6fadc7a41e73ce926a031e1724e64303efd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303dfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430f5ffff27200f44c168f76c6d624b6a266d0022e9e0a1badc0300000000000000f4ffff9f5156a718b853481a54af5cfb198cf689af0d9d360400000000000000 +frobenius3|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000045fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430bf0d2d1e68f98f523f61387cb6be9cc0419675711e8fc9d8dea647f5701acc2c2da4f9e8f7d174f6d231cabdda0a9fc598dd76ef21ff2cca4d474e1f5567de1d5595d3a7ebb2395995af36f7b6cd3763d249e151e0d14edff8df303c5bafe91f29157b217c1b93129ed677ae660940a18246eafa6f015bee08a0ee3ab7a8c30bac9d5ff4aa19505bc82fa5b948d8b1f2c63de5dbcf49d09c3e4c3b5306673a29720511f5dc793727e2f7573ea2013b249eee5b7860e0d94916693555ba46c527cd60710deb9e2ad90ea3e9a4796d7dbee0e9cad935bd6c23b50d985bca00860bd0867cf15a1ef93d1f109afdc3c7a9fe1b311a56c6a4d9146011f6a805aa032947fea2d0da68063f2acb05e3d99f4f02bceca878813e2a25267e4ad171afcd076f4bf02391f72848a5494552e860ea79b34a7d09ff6f938f3e1394dc5a93340e +cyclotomic_square|0100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|-|1ff97cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430cb1200000000000000000000000000000000000000000000000000000000000037fb7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64307b07000000000000000000000000000000000000000000000000000000000000a3f77cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430cf1b0000000000000000000000000000000000000000000000000000000000004bf87cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430961000000000000000000000000000000000000000000000000000000000000017fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430bc000000000000000000000000000000000000000000000000000000000000000ffd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64300402000000000000000000000000000000000000000000000000000000000000 +add|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|5c00747bf1e4f5d5fa9ffa97d6faa4030f86e570dab76b20f8b167bbffeeeb2de4629512b67d921aa32ce1acab3a7841c1be61c3f014b45b616e1fffb6d8b82e45073451ebac5932beaeaada471a45e8220e52df5ec97d76556a18fb5d3b6904ca0e0aadec9213768409f8e2e8e3ce0e73c813a6a547d8d8bf9999d2b60d2d119e00f98a66556f0b986a4b8d75673c3320450af62e9d78b0e72e3f667ee1df00c122455ff9891f74243de023d68e270a15d3b600850e3aa5c26863a5cad0e729e9628d09f1aeaab5d4c4e5e75b1d11a7c7b1c86b50c94de630c5195960f37a0b97fcd5aa21109bc7242adff074deb18c65a90e3d4661c9c325ca123adf3d942ae0e490d59ceb8e65cd9ff2ca6e258da344dbccf64ca908a70640e91ab5330e0903498d87d0eb8e8ccf8a4eaeafbe1d2c74fe5aab63673054bc4a41ad35447f28ff30ea8c6a8fbeeeb50772e38d6c171be751662b7fdb9ed4001617f6114a65013f132870e3c2000def11be0c82c051f1771ed2d24831e16098cde74ffbda6024|de7c5ceb41d06afbbba6735fdd4518b9fd210a104bec2d8a5afce0b7e516ba106ad829dff52932d7e236cfb7b8ec7e07b8952539936cd991456a68cf27699e05179539ae671f930fa65de07254f9f93e36d6c2ea4a58dea8771a89216aa4730bc9d01588572a6db21ad4239dc9a4fe73fab025ab08278687decca34d1bddcf2236d55e799e50faab7ebd86538765c17669419a25134bbcc4ab86bf15987de2223550f500e91cb1f0fac7466ce947a7c471eca2799ad3ca30f2feb55018f9a803ceb4d4141e43d1b8ea9fed2a1d624530e64744057f084a0c18e38421f87217178eca251e953b47bb1bd3c175578eb9831aaed6b41c0cde7a3bccb3b23e226b221f93f2cc5b203db183573760a0cbc72f01c1cc2ec65139fae6c340a8d730d804b958cfbef5e7dcddbdae0dcaa41ec894459dd4ece7cfde34236fea3f163db909867aa4a3f5168f7e3b321ed2ed449c962d982fcd724d3fe019ae094ade45b115ff29c13d7cb90c22bb17f1aea92de287e4f5a56e4884551a3ed424f18ac2ee19 +sub|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|5c00747bf1e4f5d5fa9ffa97d6faa4030f86e570dab76b20f8b167bbffeeeb2de4629512b67d921aa32ce1acab3a7841c1be61c3f014b45b616e1fffb6d8b82e45073451ebac5932beaeaada471a45e8220e52df5ec97d76556a18fb5d3b6904ca0e0aadec9213768409f8e2e8e3ce0e73c813a6a547d8d8bf9999d2b60d2d119e00f98a66556f0b986a4b8d75673c3320450af62e9d78b0e72e3f667ee1df00c122455ff9891f74243de023d68e270a15d3b600850e3aa5c26863a5cad0e729e9628d09f1aeaab5d4c4e5e75b1d11a7c7b1c86b50c94de630c5195960f37a0b97fcd5aa21109bc7242adff074deb18c65a90e3d4661c9c325ca123adf3d942ae0e490d59ceb8e65cd9ff2ca6e258da344dbccf64ca908a70640e91ab5330e0903498d87d0eb8e8ccf8a4eaeafbe1d2c74fe5aab63673054bc4a41ad35447f28ff30ea8c6a8fbeeeb50772e38d6c171be751662b7fdb9ed4001617f6114a65013f132870e3c2000def11be0c82c051f1771ed2d24831e16098cde74ffbda6024|b4766ea58c1ec0c7e0fb61005325d1e09ac641310308f7b9bdd87403ccd5aa15300df96ab7464e1ab772f02e844c91b3f0c864b51ece114bd6cd8c939f54f5088d86d10b91c5dfaa29008bbdc4c46f6ef0b91e2c8dc5e2bbcc45582bae2da10235b3012e7e0446c611c133d7f7dc60561420fe5ebd97d5d55e9970a8adc17500fad36c63d1a51b954ee8ef389c96481029b78539b510cb63dc2841499bba2221410565f32321b380cce269f55fff5adf02f7377bfd41f756c06d52c868f4a110fceeb9013ce57b4d4116225b652723e256e4b22dde75ae3fb658516f378c2100eecb73797f3352a4ec13e76490a658990a0cbc3dfdd4eb634378f10066430b2ea6c64dfa38d53f2276e2c33254eb2e80d562b4c2e244786403e49f53e017202341c1ae608228003d392e543e68768f6b185121998d8c1efdfd19cba7905183198818d08920f811a1cf223a0bd26b6d605ff46276749601371882db5dbab1e612c800ee35ccbf2b446abee6fd3617c03c5211834a6d67e31037d98632075b9101 +mul|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|5c00747bf1e4f5d5fa9ffa97d6faa4030f86e570dab76b20f8b167bbffeeeb2de4629512b67d921aa32ce1acab3a7841c1be61c3f014b45b616e1fffb6d8b82e45073451ebac5932beaeaada471a45e8220e52df5ec97d76556a18fb5d3b6904ca0e0aadec9213768409f8e2e8e3ce0e73c813a6a547d8d8bf9999d2b60d2d119e00f98a66556f0b986a4b8d75673c3320450af62e9d78b0e72e3f667ee1df00c122455ff9891f74243de023d68e270a15d3b600850e3aa5c26863a5cad0e729e9628d09f1aeaab5d4c4e5e75b1d11a7c7b1c86b50c94de630c5195960f37a0b97fcd5aa21109bc7242adff074deb18c65a90e3d4661c9c325ca123adf3d942ae0e490d59ceb8e65cd9ff2ca6e258da344dbccf64ca908a70640e91ab5330e0903498d87d0eb8e8ccf8a4eaeafbe1d2c74fe5aab63673054bc4a41ad35447f28ff30ea8c6a8fbeeeb50772e38d6c171be751662b7fdb9ed4001617f6114a65013f132870e3c2000def11be0c82c051f1771ed2d24831e16098cde74ffbda6024|175f740194363dd0887897a6a9651bbe28fbb66e3ea590c8b017483ae501040c89fb63edb66734f42ec87611248d62b50de2b5725d9f56b96824b7b88ca34c2f2401dabed0e8508ff64b2fb59b859551c72a01289d1fa02e82cf17e10fd12c020ecec13b68b522829932f4dfc215dac582d6b371812074dcdfea22983d7320293d3e4821a36465d4f72c038b5fe93a6a1bef9c62863af1115999b06da24ad029c4870b672dabe59872a5615003169157c99cb8ed865e048a2fbce4f80067591eaa2e9bdb39dc3a30ddb073cf07711fa76d23fd821d660084d83a4e10c2812d16d0e6c65578aa2ca181cf2d026652a32822832e04f3d1cd2b7f53d556c773101b5af75dd3af2a39b535591123b6fd4d428c17cbca4e222fcd3fea9ba807e05f242431593182aebc787822ee28f37de26ed33ece2e7d2a79d828ecfe71a2644309ea88579dc7a1c602ddf6d8b53653c805f7bd52379d869ccdc9a46764ff78d719cb67a718318b9a21b654e3b79a1816320bc92b97dd3f73a0c012f38a0f511b25 +square|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|-|75a0b8634ca8eb517247cb8eb70fc453700f96d51b470b3b30f78fd4f10e1d21c5c93b9b5fc484694eaf567ce3c494eaf46841ff81dc1403a765ca81852336041c9a93c785ea5b6a0180f806cbe3fd42790bd2bea873128dc0c19dc7bd9fda142e88ca6fd2ab7626dd941d201e7749cdc299b4bbac2e2926760fa5955c03d81376ac4bcd5feacebcb08ea1cfddfc0bf0d2b1f90e7c13973bd0195f7ef881d019018a310c429c18b94701fd45b659733c03234671ee820895e29dab625704a92c49007ffb56b930c43f412f3a3cb19901e4efbefcdebb7440914e33c95a037c11425db340aa2b54eb6066027490ce7d63d0fe6a1c99caca77c665dd888a3efd055d24402f92bd9c46e6c7fc1bb7aef35c8ee44d9709e41efdaba6f1ef46f1b52475484de2983e6cff8de50edf33b58b6c3410819738478f1a6106d60181e7902d0937fb646312d56c0d323c8aaf2d4eece5195918f02b2b44910d25730901180e800bedeae096dbf494f5fb4bd4189e7f71c8cc4911833fc179750e5fa37eeb00 +inv|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|-|f56d86f017be8bcda7c78a78a529fec6f683dff731fd6993f8c65238d85ec5215b6ed2854db9c5fcfd7c33e86a8dff4dee55790e026da2402f68808de2d21d0aa31d9a0f26793dd2eff39a893609b83234045415ae9b572da946ca0466455121ea04877db2687f88bf1862f83ccdcf90b8fca9c8806f7bd5dae7832c141b1805f2bd62e2639b9583bf46888f698c7bf4a135df2fdacddabfa9e1ef2dba320517674507096b2b0e0edcfbe0949758292495b7d2ac5f684c4039e0c7040db6901766dcf31bfe5ceb4422d1261d3c44b90fd3fae4c1b55ff3b69f0bb945625ccb0ab1b111b9b4aaf3a865e26dcaee141988b773b0b9c91377a6430393e8f424081f95bb242d474f8910b03e465bb7e5e4adccdf31d2f020bf6cc76759ac5780501b47f2849a0f1d9a3e1c64ecdbe1ac9ad04b9bbac237e5ec23115ae797dd50c025acf1b10deca74a74fb645b93d58a33c9b771d73006855c07b1e6a845e0d9e216f4895b86735685c7c142ed45d37f1d34093e4fea0cea6b057da99adecae6f21f +conjugate|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|-|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250a62ab35cde9f7f93877ef6925d0254d0e3fc205e8870654924282c618dbcec7240932b08c8cd4530c09571d7b1d50f8084bfb37882955eb48eafd5e87a01b2908c1519e0841cb51b44948bb6ace59c573431a00c88657cfac1f7ca872dd0236044af0bdc8da03b2ae11dc40e40aa055972e6186be7b97511f99db566d1f07c61ec0b3c2c18b0450ac07a0c5793192fc1b1712b8dfc2d3afac10083f8da652181c40e966326709f4ea33facc5dd8926f6993282c6400ad8b465af9c25e7018720a +frobenius|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|-|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd587632137a8a6b33c0536043c0f511f5f24df93909293c8a5da8dac91b04b72f8f6f1a29f89e02ad041b1eca57ed02d3f805fdeb82426f1bfe5e91d5d754e2d7fac535111f81a28a1d9f79a6437267e3ad8d38adbd95ec0d08d5a0621cc233fa2bec43119b7861580e670a83ed15decc39860e489727cbcb6e2d75eac2d361088666ac026427b7c6c642afbde9dc14814b876397350d7078c459fcc1d615a682bb579f253872bcb31d8d0b9db055d6aaacce9dd33a121da61b09e0b5d01aee5171b6021e0bef54e573f3af65ca12494b809937b5553cdbd082747d8e8292f9244569991565ea4170eb4dcf689e4a543b5a9db2daa2775ce1df95b47ac1ea228979bc861dd7945defcc2dae8ab1810337a19971b9940b7be793d08aa06aac171e77323902f5b5ecc88057bd1f284ace610c259b0695414868ffdcbc3a9c6e70d2ecf480284e562dbcdecfceabf9d14d10422bb10374f2d13f7cd780f56b29a2d363dc9f20 +frobenius2|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|-|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de490706a23d4e53157a55f5c3421941be2472530e93b32bef2f0b3457d5f8bb21f42a0309718f9c73cad93f2aa9591ad076cbcdf6b8d85f40650bb79c2db5c3f9c3189763d559489cb525d889834fdc7405f67406bcc81854d537e4e686e5389f20170db92dff620c42966822fa5d28d8712f530de2e3eeb8d98269d6fea56ca07014cb233a2c26d7a299d7cc6ae1fb0fb25fc9abe4a0f2074feff07b8ddfc54b7404e8ef6aac24a8a184e049f962aa53d6d0ef00203dccb0e7cdd14c2652593ecc19c1519e0841cb51b44948bb6ace59c573431a00c88657cfac1f7ca872dd0236044af0bdc8da03b2ae11dc40e40aa055972e6186be7b97511f99db566d1f07c61eac14bdb795649d4be9d0f4f0cbbc2771ab3a40ab10390fd24d789f809688f210d67ec499e704be9908275df5120c208cca785f887c48d13b3cf6ce7d792faf01 +frobenius3|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|-|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd587632137a8a6b33c0536043c0f511f5f24df93909293c8a5da8dac91b04b72f8f6f1a2915c0b199448b1a4b289ac662b740c92b7c4057e003b4575dd1e82617bb7b4830d887497ca98b9d6466c90f7e78d0ae7212184ce9b59cff10e2e06a270bebd713ad537e0355542569cf4da4ed99fe82e1f9eea38290f4d0ee242c10a8b6c3070bc961ec870b7167ec77b167ea2ca3e909190c157d9f7a95d39b466f3f161bcc17ffa0d111f13cfc219bd8004f9734c818e68c926e87bb3c417f2828970719850502503a8f5ccd0938517d3940c4dd1d1d340838780e7daad2442f239c5c80691ce2123b682b3e51d3ee7f1d2d37cdcebcbae024a0d6af9b3d68b50e58f991dd1270681fe9495e72b1db486e31f0d00fdec84c069a2275c517bff319c3fb1b2b2ea4591b3b04490ea955b9c9974e1f8bf3a8c35bfda22d2412a6bd736e5081810ce2aa04913b4cc80c6819be55a73b3c9eb2e07e76a50f3d3b3fb876f4708d7b14 +cyclotomic_square|c9796548677795614ed1ea2f98b5f44c4cf4a520277a12228ceaaadd58763213cd7211a55638c0f8ccd45f739e1c885d542f45f7589d75ee0d9c7ab1e3de4907d28d055d7c7239dde7ae35980cdfb45613c8700bec8e603222b070260c690a07ffc10bdb6a97593c96ca2bbae0c02f6587e8110563dfadae1e330a7b64cfa21198d465ee37fb8aa0e6523bc611fe844349fc8f2fe4ad4314c45780af199c0222bb2a2d7a061fb2b86355d8b0a4230152ba716dfacb0ae1435936848cc076250ae551470b2d94260316db0743c14434891e967b992e3ffc25e71d6bc8977f9c0b3ecbcc4b8ab7cc2f847354ed731a898e125d49f98cf0646f3fa2d259d2323b2886abdecfd5c0ce874382b6fdc210bc231a3e81b92fee800b0a24896e954b2e2cfd0cbf0f3c886e8d7bee308486ca2b002ff7fac23aaefe9890c4da7353479e118749ba168b87d08f852aacee5fd8847b4646c9a1f371a00b1998f253ccfb4b14071416a6af822c5159d0a40ab9d7112eca2f551db698c471cfa66e820236f225|-|6dc65fc383b509dec7de3a63347379b2896f27d715d8090b2febd3752891e82e63b6356e5df4e57c3290d8635af2ce65f076705687c9e73f58efbe1a8bfb4b2e6c214786095c299ef89fcbde209ddcb13d56ed74867e3b42a77e063ca30fb902a9bb6c6ce027456e1587a0fbdf975d17ae1b5c7e6f7f6e227ceb73159998ec2490769701380dfe8f3bb77e445a912cca8bfc61f81b77847eb06a9fa2c47105056e48a587dae549496ef5d6f702c63e3c9209ef2639d5999250a7a032eb42bb1f761572ba4e1fcf4fb93f80e31930ec5e9786a4dfec51dfe474db4e523178560d458baf72eb864ad36e1781ecf264ca24c331d83cd9702b8dee3bf67deaa2002e4c5157c7decb1295ff547b64d9582f182b2729f5c436bba0c4286f50a4ceed055a8682119cc66920248f81d524f0c80338b0425addafb9f54eea4b9845b631283ca8f680cca1aab002769848b272bfa85a78c750ce2f294ffedf4cde25d4a51f27a617a37d873e7ecd52c3eb2bc2a5e40a5aaa4557f118ef8886f7c10569361d +add|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|beb8751ef317ad559a00b597eea06b5def96f8ac61be91f8184780070b7b29300bf5449d89ca3e865117878c87b637ca9046ba8be0383013609cfb537da4011fcd32e6659982b5f2590f93d06cfa422be1bdbc56a81a456e3d4abc849734750c8beebeb745e7fd737cc28ce701ffc2d044d2a2d4fcc59b20b267fe492c1dd801f50cafbf7721146f94debc8bfb6345f60757e55c072613e4d02f64cdb6ca7d0f62bd9afd9776ee253de338481fb78f7c37c8ebe1e9e5507cfc4c48017f423905f9e8d684b6dc710c6deef38d5879c23dca9f982609d7aea21ae5fa6c44929b0e7c2f8b3eff42d01351729aa2141ed93178355c9f14378252f8e2b791e0387629961acf3c54d442663e4eef14463211a34ce21973f20741a5aa89b7c19f803b13e51263bb20f0a44a72255cdb090272a9b4926a54a6bfd94d601bb0c82f60af0724389a2e80aef55a637cf4496ad36666f033a96cda4a31a5be91479681d5710c015747f4381806fbfb357ef49e9cd314344c63d386256abe546703d0de352c0c|547097171c2362df55490f114597914478bce1e163c02afbb7fb7ff95ae69d151ffb0457d784d3f4241cb2689eec839a00873280903784796fb3cd0a7a26b816aded9d6b2b221f9423abf90a69c5044d2670f5ed8292c08f46b6dac5f122a127ec1c3e973050050f3abb4e06621fe48f4bb8f43193e3d2887095ba87a03c752db426b2ef1884360807d9d9bbd7ff1e6dd3bb14c97a9850a1126d7ea2e0eb5420811a0655c4df4a2c28e678448d2d5ee84297cf3cabffe32ebad4c11b96ac851efd82d75cbd5bfb3578c81756e9dc36aad17f003efce52a5f334326bb72bbfe2b0e4f5665c676d2227d990ce7f4f993756caea716f399dd5a458dc6fdbcbab50f55685828d363798fad641b4a9dc95475fe1e3e1d8587f16de158bc8c072ebf2d1352bd1f5ad58882329a40823022e6feacc5aacd5ff6787933bdeff38c94ab207b02495ea49d3e18e17493fe2d2ec0b208d532db55a1a4b278973edeecd906185704a086ad6f5d70c8da8f28ac637805bd14729a28a30cf197c7d2b927fe2a2d +sub|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|beb8751ef317ad559a00b597eea06b5def96f8ac61be91f8184780070b7b29300bf5449d89ca3e865117878c87b637ca9046ba8be0383013609cfb537da4011fcd32e6659982b5f2590f93d06cfa422be1bdbc56a81a456e3d4abc849734750c8beebeb745e7fd737cc28ce701ffc2d044d2a2d4fcc59b20b267fe492c1dd801f50cafbf7721146f94debc8bfb6345f60757e55c072613e4d02f64cdb6ca7d0f62bd9afd9776ee253de338481fb78f7c37c8ebe1e9e5507cfc4c48017f423905f9e8d684b6dc710c6deef38d5879c23dca9f982609d7aea21ae5fa6c44929b0e7c2f8b3eff42d01351729aa2141ed93178355c9f14378252f8e2b791e0387629961acf3c54d442663e4eef14463211a34ce21973f20741a5aa89b7c19f803b13e51263bb20f0a44a72255cdb090272a9b4926a54a6bfd94d601bb0c82f60af0724389a2e80aef55a637cf4496ad36666f033a96cda4a31a5be91479681d5710c015747f4381806fbfb357ef49e9cd314344c63d386256abe546703d0de352c0c|66f9a58b630b49ac3bdd88b28a2abdb8543ff38a0dcfa77ad9ade2ac2a8d1316500ef8f4da7b76240fb815b820ea959d3c523fea850b740bd91a0844f22b19091388d19ff81cb4ae6f8cd3698fd07ef663f47b40325d36b3cb2162bcc2b9b60ed63fc027a5810927413635375e215eeec113af8899579b470cc6bdf34702c529ca0c547029410e2ade1b60a4e0379480c30d4a0f6c4c2ad9700db60773565901bd9fd05994f26de0ad1f07b44ebf3eefd306f878d7334236c13a3119982713140bb1295350a2171d9eeb2f3a38eab12e3d40cff0e937cd19fe7830e1e996c70ea4ea3999f5087373f549bb72ee92e44037f4f1da36b77926a807ba9ce1e5911d2933baae2abbf3c230c83c201165322f655a0a37a0776f238c454d09c82c4807492cf7a818f53eed4d4f88cb1c1e02ac43a0d5241377c5dd72868f622dd44c117a8f91d9bacc739ea7461cd3eaf1737d85c56183575192202514e1925c7d872f5556119e3b3f517ad06e933f6e2ad1db547cabf31a583874eef8cb196a92d214 +mul|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|beb8751ef317ad559a00b597eea06b5def96f8ac61be91f8184780070b7b29300bf5449d89ca3e865117878c87b637ca9046ba8be0383013609cfb537da4011fcd32e6659982b5f2590f93d06cfa422be1bdbc56a81a456e3d4abc849734750c8beebeb745e7fd737cc28ce701ffc2d044d2a2d4fcc59b20b267fe492c1dd801f50cafbf7721146f94debc8bfb6345f60757e55c072613e4d02f64cdb6ca7d0f62bd9afd9776ee253de338481fb78f7c37c8ebe1e9e5507cfc4c48017f423905f9e8d684b6dc710c6deef38d5879c23dca9f982609d7aea21ae5fa6c44929b0e7c2f8b3eff42d01351729aa2141ed93178355c9f14378252f8e2b791e0387629961acf3c54d442663e4eef14463211a34ce21973f20741a5aa89b7c19f803b13e51263bb20f0a44a72255cdb090272a9b4926a54a6bfd94d601bb0c82f60af0724389a2e80aef55a637cf4496ad36666f033a96cda4a31a5be91479681d5710c015747f4381806fbfb357ef49e9cd314344c63d386256abe546703d0de352c0c|61c760684dc6563c10b7cee95721ce8b796a01c1cb0027c6319e8d6233f41a035e088cc9351f4aa71f5f7a494cffc5e663109a0abe7cec657cb5a15672626e07c5b7d4a20342b335f325e127621209a70c0f1809e81a777995a49197d213bb29700c7ea4ca1ca98ad7eb62f9ff99792c1f5b2b6a57866651a361da00e752242aaeea7e9d45811b386282658c70e1854bc64fa3848e03a18eb13fd142449ce716cc9813fea02422eca98d643f9af9877338274256da69f9dbc77ea795bfe4a519bb09223356a950aaafd6f63f39cb1697d9ee1d42006f450bebd959bfbd982828ee8f52ecfd97c051febbfec12e83ac707c95f6b1311a19855abaad34050ce113109db90e448ae5aa32eb33e84bd86e5666e0cc0b47c73a48c3cf7e37012b7506697c7ad9d811d2127e2b796c8f8a4f36ec66ebb5d911b7beaba8935a2741a926c9deb8499c61555cea30ff479c98aa87e1cc76921a15e7f2133c587177eef90e76c4ff9a827a48a9e1c4c506febe0a234f16ac2af6484a10ad756cd4227df615 +square|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|-|ac9a6abb4b43437a1dfe51326611ca6bc6060cc2312f2b8045d7f412cab4251810e08875df21a0607e5f2a45696809d48afa4da6025a593f9d42b1a445f3051c9252eddf267678550296f237ea9b183ba9864fdc63ec88b9d8d60837e8e1b91c7cc8bf2d693292577e2aee7666e4dd32842b6d3501aa704127ac47658a6583244d2ad4ec7d09641ef3a514baae45e1759406864fd99170719506c2f5372f231abe7da10ad78db086beaedfdd0bdcc25f83b8509c2f5a28be593eb6a8bb37d5211d2322376974dec0752ff23857ad3e6b3ddc34458d0b5dcc6330fd3daf4fbd17ee1a84a202f1bc6d9ec17c21e3c91ecb229b045c6573e7bb897f2157ffeb9406250cfba9db08cd9d973147f4e2bb4fd5e9a7ac8f99079fb508ac6ae8ac6c1616ab70f6209ac8924eaa6263471948bcaae0aa479770d9b0f0d746cf875cb0bf1a8dab45cfd9db232730fbe25216fea9346424727b1887fbf3c0c7ebeb4c08f9002a9640203021d6463ff26fddc7aeff3f8e258f50dcf44e2f892daf8284af670f +inv|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|-|54330edb3701b8fd8403db1115d2dad8e24f328c51d71ec56203cfd213bdaf0ff80cfec76e7a377522f1873920449ac6ae4cbd501a23abbe27cf39a4add38f239998c8a89564cf07e7de15caff551dbefe546a94fdc2fe169dc9e512bf36bf1e14a15b0775417f648b80be9274ac1b381a8eb225f3328f4d14a38617e0150811ca0f4163384145b8fc2de2326a6cf0dff7f6eccc7fe3c9eab59a8720d8ea1f28744697e73d71d11634e941a9b8b1400baf2669d09ce0f765f76acc3e8679bc0496e78fec66a106bdafa425dd5c0a77959ecc8b7ab05ecb18391888aabc86990dfbdac25df75e2e0f138d64da5c9e0e02f49a1100cb53ebf2e3ff7a4c373fce2dc88a57bc995e51a35206150be222e655c77b4e06ed3b375786e2719af5af1a18cc89329936cb464674ed8e68b7a72688db6fc357be77d62251d72552f4f5e805f6213cb030b78e2ccdefa9f168067d6bf8352a98cfd8c12cf4b338294fceef2e07a8de59e895ab677dfef15ed4577459841230a4bd92012a8386fd22b1b05023 +conjugate|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|-|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c1943637c00100d971282f04da000070d2b5678196ac336d4fb10420693442501136ee034d938ccfdf0d3d88dbb1f2445bc0b87b488219da4f7b255f193237ec01988aff3ec97fce9121eb445333ad33dc5ab1b5dd723c69feff2d02c160ba1e01519be2274dda63c04cd558dc16a4a0d4265254108fd0eb18c56fef1b5151a6817f032cea8f29cd77e0fd2d2b3cd0f284b45b7f7123befdcaa6f9a3a99074acf24f14f2446a234c9c6c025603484a3dca6d48f72ba14c8ad85e63f62f72986650f +frobenius|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|-|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d815ecf93f46b2456b912cfbd423e9c9b32f90bf870b5001ac99f0e82d49037e4908fde1cb67578a794700fa77e71190a08a6af6915cca977ff94af008202099721e20d048d75f086fe6ef618279cae89f4a504a210920d1c5ecc91695e3ddcce502e8495931ff7210e54621bfc4e3aa2180d8bc802640efdc0dc3a893acf8c54d0d1a1845f19825e8af55db103edd4079bac805d4c2a355da3cf6af713e40c456104bb6cb774f97ea5745dda36769cbf9012093c921153b82e32fd5b7bd990ee30daea8beb7e217961099748efe526c5413fd9409443919768260c607db20a9c806bb1a47fbb3d2bac8c0f832a5400d42e6f0edf42dad3cd7cad4a96e2aee45400d984e4498d00436a063fc2527d990161fb1c33a598f8dba7ec223afe7ef11bf2fd4da6775a49b6830e5f1e0b05acbd8940bba0fe7fc11a683aec89d72c9405a23baab7791a9466c39f1457b15aacea30dd969bad84b7e285eb867302e763c8624 +frobenius2|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|-|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28cf17bea28b8dc03137f8945f91c9d7d3fd4d0f9cf8d1f0589f788ce7ad28991d2e65b11a083d4c10ab7ad39f25e18e90804372181373d25a6fa6b03a99939515e295481b6f9536d6565fb388a4f882a9c8ff8d11f644d7024c3141e3467ea5253b1319baa26447026c27ebb4da38d0b2f8a7d5ad1c491813107070ea05bd01021d79bfed9b508155118ba3092ff6b70bf3da74ca76ee6ab304b6103e061db92b8d6c13197bbc80bc615cd73fd82458e2cfeca3e3456cde0353a3e7989803222388aff3ec97fce9121eb445333ad33dc5ab1b5dd723c69feff2d02c160ba1e01519be2274dda63c04cd558dc16a4a0d4265254108fd0eb18c56fef1b5151a6817a96a1556411118e670a8524079013acf007428289799543ebe646910fad4f71f796a702683b1f869ac88785da14ae5aaf543e9273604e021c66ac5dbafbd9423 +frobenius3|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|-|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d815ecf93f46b2456b912cfbd423e9c9b32f90bf870b5001ac99f0e82d49037e49084086262505521b8b14930b45ff87ffe08b4e6cfdd548cde2aa5f966b59566d0633fc3675e5ceb8195a490792675ec9ac13434c4ffd63de974f9b3634379ec525fc2d7ec638871f14672c903c7482864a2c3c2a0074cca4e1a4f0f91478336409550310b81a9223227aaf91b3a1dd2d55015355b2ffbd56c3c2990047b0748c21d71633eecc6dcd23feff41dad899f2b9777bbb9025f8fa99dc7aa19f674e231df3db7a0300e242ac45331fd94a9fb0313ac9ea27fcfd2fa533571e4c029aac008ce235dd62b96573ccd13ec3505d3fb16c6a8c53090979ed54f6c2b684082423afae38404687ea9b29ce4b41b8d96a78ac94462827b89539677c82f9823ca500ec43caa3518491861e32a1cc2991137bcd5a567aee350d601bfb20d51e2a5928b179a0a3b32ed11dc3e56d532eb0865b39ce9ead3aaa1b6fdc1d3b63286c1a27 +cyclotomic_square|ddb49ed13f97d5c54813cce1e760a77ee67d6ab6b847e9bac85431d3c2b9d8155b033d926446b5aa60cf9c44a8a0cd67cd98f9756644a41e39b703986fd01a28e0bab705929f69a1c99b663afccac12145b23897da777b21096c1e415aee2b1b612e7fdfea68079bbdf8c11e602021bf06e6515d961d3768be2dbc3d741f9d2bbf190330a162229972fa1c30dc9bd976cb642f6c73723dbd413d1ad52921d7101f5d6b572c695c06eb0240fc6d76ce6b0bcfe35ac11993b2bd87791a176a4c19049a00d8067f89290bda23c89063746c07e06717f30e7cbc185e2b4e2e29631dd91c48ffddbf224bb9f1e3ac71463cdb51d1ccf894a8abc0764a404d4fd0a316bf4d89eb7e8f36296f162c35579743d2b13c24aa927fb0c836cf04cb67ad831a2e3f5a6439e5e337c074e4a626207455f8324079b9369f2bd3a13f2b5d34fc1857caae2f24ef48bd7df89eb4c35a594c18a1896e7b56730dba05f7476b04950b56ad589274575775cca411340dc7a4f088c80ec7a17da2324360cfe948c8fe20|-|d4a9d03e97fc1862286f802dc42354b00b0788cc75603b916685e8a4991e341ef3745a6a80f42a51036608103e8fa4e131ab580fdb26d6979eb50a058531880fe5a265c43d6b74ecb1bfe2a5c6d4b4566de1477cbe707e7a3abb1e6574f5ed2c092f4d629629bfb95f40bf9cc6962b00b8ef25daf8eff50368ffabc91832b60a631a79f702958e49220b5fa59cfd1a61a9fe0fa5945f28b6cb435c9d5be1e507c29b215969b9f46b80e6f85087adeb6a719d43e405c5e36cbd02965143e12c11da9f09ce5c11262141e2b8d61102084c83abd2de145b1d60a4f12cffeaeee20cf8892f257764fae84140f9c994e9459a6b34e89f3c2e058693bf944787032e30490dd19343f11f8acc2055b48960e58c3c0cbd48222e47f42c9058618a0b53197c6194068256a41bbfd44ff91a781b9b79a65d4f3e40b7ba0e5ad82908a5f425e4a8f27a7495b7be905365d10d565232106dbd10586d6d35671382b060fa202e29a37892dd73f5e9bbb35e80f326c2c6174bee5b6626341c45fc3f7bf2d75a00 +add|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|d5295c849a2d68c837b469dfb25743d14f99b35bea84ba94467251dfa7984c269c515ac95c8622a8e6173f687a332f8a977b392f95fe6bdcf865cea256f867089a06de1d46a626d6c9d7d0c1cafe0af2efc3c5358face67260e42cbfaa0fba2328b8eecb9b228799fd40a51423d21f23a04f3729a9d1a35e4d28b4ea9e26c915a2b5cb53f5c86b65424121cb03c21961afea784a7ef520d5ec8586c3b928f11d35375edbec2bdcab4214946b7a7c0078dcb18de50148ad1484c2f7e5c38e300492caeee8e7550d6e7265967233a88f672c5eb1b1cd8efc6eb8462c49201a7d13b2e3b969a6ff365c26c458af0c9a2684e93629d6d4d96aa09f9f8165db1ec91928bce4d04cf5874159c207775dbc06947a30cd54aeb3854e23c34e9c7226ae1a03c2cf90a2ec7c6b0b36b01d5401cbe79004a8bfad767365f8fbcab5304d0a1fd680f064e479e35870814dd0d0230b03ff6bfdb1d93a34435151f8813a9fdb04bb78821e8ebb2e87e03b8d9d2bc1f482d7743b822d34c92e5930f73bd4f20e30|81900b31514895041c143242ee3c07ccf7cb1425fc9355baaef633a3e0407c24d94005e7ff8d7d7c51cde6e9dd01fd3bdedfa24ddab04d8578e81b2f611fb429e9e4f4413798a42df8ae872ae0c0d352706fb7cce31779f9b5d193bb5218d919415c2ca4a9b7bd5a51aa804f2d5c838178fe50b918d863f36bffe74dbba5cb109020cfa659cd73c18af2297b8ec90f0d1f22faf0bdab510d898fe0fb79371d1e7b5e2e6712a0abac009acba7684fab276749e7b593161222384cc54b06d76e0413c5ef16a1d7c179dcee7e62b36fe55e41d1f91b2ad3ee143d013306105ffa0b7eca74d5d72d0b1926fc32b574ea6c53d034be430111850520be72b46df3ef2f9858f4eb374b524fb8519136a78cb98c264ee49ca627956e8a819f4527a54506b7fa95a06c66dc56684acc5f6574370263f83a248cfcf7c35214322ba2f7c12ec019cb0fab34b76f690465be3072083edaa5c9a50056d252b0a1accf58a03d1558f35fd7d176ad98bb08a4e4618845e7ab9e051a851fff5b8fc68784420ca813 +sub|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|d5295c849a2d68c837b469dfb25743d14f99b35bea84ba94467251dfa7984c269c515ac95c8622a8e6173f687a332f8a977b392f95fe6bdcf865cea256f867089a06de1d46a626d6c9d7d0c1cafe0af2efc3c5358face67260e42cbfaa0fba2328b8eecb9b228799fd40a51423d21f23a04f3729a9d1a35e4d28b4ea9e26c915a2b5cb53f5c86b65424121cb03c21961afea784a7ef520d5ec8586c3b928f11d35375edbec2bdcab4214946b7a7c0078dcb18de50148ad1484c2f7e5c38e300492caeee8e7550d6e7265967233a88f672c5eb1b1cd8efc6eb8462c49201a7d13b2e3b969a6ff365c26c458af0c9a2684e93629d6d4d96aa09f9f8165db1ec91928bce4d04cf5874159c207775dbc06947a30cd54aeb3854e23c34e9c7226ae1a03c2cf90a2ec7c6b0b36b01d5401cbe79004a8bfad767365f8fbcab5304d0a1fd680f064e479e35870814dd0d0230b03ff6bfdb1d93a34435151f8813a9fdb04bb78821e8ebb2e87e03b8d9d2bc1f482d7743b822d34c92e5930f73bd4f20e30|1e3ad0003379e5af3976d0eb19f801c1b5f12eefddcf30494bb2c2c5035e4708a19d50544681382c849d6819e99a9e27afe82fefafb375cc861c7fe9b32ee418fcd4b5dec1d777bdf1c9570fdc2d3f06ee3fade27b04fccb1ea96b1e7047c90238e9cbe488fecf63e3f2a78e7822c5d295b763e87c7a6ceefa4eb159f0a69d1593b2b4d785c7bc32933a594d18b05de21da589dd7706601bd923055679349f1258edee884fd41391083c153905c12bcf0b3e4d6c46cc07b159670761f107722c362d8f1de8b7c7d984eec3e5dd894727466d183a45fb45eff5130c554279641561007edaa1babd9c663ef3beec20a1e25a1fed180ea3ff7c0a1fa1ca2904c22c8fdda722b5ec62089397f3b07d7e2dfc8e45cb740006da896d9b33eeb4a64d01f87373573e1903bcdea8dd8c4edc22ca9e476c26e75461b18bbccda0b3ab11211418ea45e240f0bd8801ca1d8f2af237dccdce414de069cc0dffbbcbe361860b70fc544be317910215266d7a2ddb5e10b865911897420d6f30a6fcce7fc35214 +mul|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|d5295c849a2d68c837b469dfb25743d14f99b35bea84ba94467251dfa7984c269c515ac95c8622a8e6173f687a332f8a977b392f95fe6bdcf865cea256f867089a06de1d46a626d6c9d7d0c1cafe0af2efc3c5358face67260e42cbfaa0fba2328b8eecb9b228799fd40a51423d21f23a04f3729a9d1a35e4d28b4ea9e26c915a2b5cb53f5c86b65424121cb03c21961afea784a7ef520d5ec8586c3b928f11d35375edbec2bdcab4214946b7a7c0078dcb18de50148ad1484c2f7e5c38e300492caeee8e7550d6e7265967233a88f672c5eb1b1cd8efc6eb8462c49201a7d13b2e3b969a6ff365c26c458af0c9a2684e93629d6d4d96aa09f9f8165db1ec91928bce4d04cf5874159c207775dbc06947a30cd54aeb3854e23c34e9c7226ae1a03c2cf90a2ec7c6b0b36b01d5401cbe79004a8bfad767365f8fbcab5304d0a1fd680f064e479e35870814dd0d0230b03ff6bfdb1d93a34435151f8813a9fdb04bb78821e8ebb2e87e03b8d9d2bc1f482d7743b822d34c92e5930f73bd4f20e30|c8949dd1062abe44370f89ccf798584f6862076a8dd6b8c6a03fa8b3b7e05a0c9417ffca0832190f1a32e48de6fbfe1b0bf5ce3c45a906b9d969844de7ef3a1fd753d039b35b1de46796b1f161ee91b3ee55c2cfac30b12e62e61b6cc0bde405c598e6ce0a20cdd585183ce4c8248cba4ae9c2ade082deab403938772cb1db26d06f3e34442bcb757eadefa5e2022ec28ff079149410267f399c226a42826a2733c0511b1eadd9eb92b3471cd2ba5515e84acababd2b94784ae277350bfa8d204a68870b489a3a4a181b2d54eb55b189d124d8f4c580f29d33dbd01686c9762df67d7fc2cdadd0154aafbf20e94da45086a4af4c0092dc716a42293ad4d804182c6db668b449ca8658e41fdfe6b439adb6f9f85bc4ad78a13b405aed74eb18049f410d547fb767616990f00b93cbb5a14978f6f141ea6a279f5431827ad9410063a28e5e8c1b2d69f4dc59c5c5e4c043e072f602c202f829d33f02ed50e17112a4fec364d365b5892691c871f8512dd7304fb20d7e2aaf167d0c104ab3244203 +square|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|-|ea24dcbb9e21ef2f05dd52ccdaa29c5ceba9448eec0f5c7571de2d616dc91a19d927b8b6da808aee32e170cf8477cdcdd6eb642fa07cd314f519a1c18c560e104a8fb3ce8fbca3531d41e7a4cdce7cb0737a50c592c7f5bd9c3b75bdb325e20099f9f36450889d391190dccfa4bcac1675e6c35c56e5d0a530747a85ca705b0be6ad1b38046db349190c769e4221dddc67a91d33f7d5f95dfe5602078728140f489ae0a7d4d2778c95876aef18fc9af995ac76a49aa42a7f82d477a5076f29038a8fa32b8bd95454495c713c2e7a30294b000aa9a45ff9b787a3c84a45e62b0d9c07a2f8b603f2beb870101697a30c261072d60d983de16567f223bd14cb4c16b200dd020779e8cd4392fd9d240c0e02522cf667801d02a9699e7f5ea21ba51e64b40a82eb8277596ed42bae3f6304ed74f27ea62a6df10a73ceabaa738a74287038531c8c477c5339ea86f4b73e8e4835d188e33620dc82b1d9b56f561d181015d47e862c5d3db01a8548418855b6bb998bc68f8db26de0cd879b5248747617 +inv|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|-|18f5d735f36c889504372aff4acef19fb0a0b682ed043ede66ac574a4b82fb2fb921555b7030fa81222f4673af414d1b65b74a77a22dedc6c13e894eeb4ddc186ab9917cbd5607543bd5088e1ad97c89add984629baaf459c79106a9cd725826ddd952dcd5fbd92575dcab47aa911a1f008d1934c27abfbcb4328355889e732f90c221e75ac720ac1c9e8b46b22a63385783262f6891f5eaf29e8a7e6994740451fda10bf7be31dd28da3b6f4793443a7fa1020183f0a85ddbec0289ce63eb0f2967fb0d3628d889de626f210d879839a7702de5c598c38db910f5eeb9d7130bf0bdc8825507d667801fba73fefe8b314d2b878dfb2159b532366f84279a0f032476e887ce9cf351814c182f8e2d5a20380a8091077fd0d94697d81355e7992fc5fb33209e5195cc9c546c91ea6839d6be5a1a99f0c08e91f24ae1a193cabe2e0461decd929eaf184bd5f605f62fbae276025efcbae268a9935e9e7ccebb2303e0a2c080108ad03e38e9a22b900a5b66f8a145631cded2c8eb337e83da728929 +conjugate|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|-|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e007f05ffd1467e4bf4957617108038aa08eb8cb795a3bb0d5a7b45f94210bb82077b16c26ce55d4c7f8d929762291a3bc8765aec138a0e3653a9814092e0793d1a9063f0e414aa35f2a0707640b62f4d0754e2e8b7078cf0df9841af564b81681493c4b6c84c12c15030b6552680f7147d8b64ee1cd8bfcb59cf87ca6b01a4ac205d64a22d50d14c2594475a7a311c845c821eb58d8f2ab2a8ca4f7d93544d022063852247bc4481ee2433e9b8c938af9b2bd63568a814cad2c9696fb791e6661c +frobenius|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|-|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e0a0ed2ba7384c5672215cae62d9cb3e516f4176371936e0faa1de4546827180fa0471a6133671fff453ec024389f9f16c1cead1f9d4cf8291e8354134917a02f4c9408abcc6fbc2251a7bc9153376aab9a1fc74e49d5b76e1ee3f04295f9332908863a4886d4ca251f3f053b51bbab711642c2735a7dbad99fa057db74a5ac226669e8afd1e7c1e8dec86f10b36aa3becb368d5e457eb2b24c16f501ba2045178374b0ab533e4cf75221087c5bcdd5a941fd9b0b402ed21ce191c3907724363020278b55bb0ffb92e8ea15444a6b26121db9fe5addbd9e5ab55a75cf1d207f004a303912fb5f57838760a1fa90b41aa69a73d7634b1b998f4fa588434a4a4a18ef7bab3fd4c4cd93a4bf29c89896b22f0c0b0acd0a68cc0adfc5bb8ccbeaa52664595bd0cdec2e0986a949e6077bc2c5eb31e639d0d13b620169e0409908ca08c77b38d1df72c545e784de02d537ef7c98c7e1920c57919af694f0ffe569d529 +frobenius2|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|-|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c21e4424772a1edaa8ebe636d449984008fcd4d6f53fa552ffe9f6cd04c728ccc09de509094a2275edc4de9ef3a136084ad2c50fd0db2f24687a1a89b9d395ebd07e41b295eecd59131e0a743e624ac2328eabfc3bc75237c9ed6c17f201ca3751929e5eb581c30374005055125063803e81d110f2e7a1362f05f19b4d031b58e2897de285ddc1b61db0caa1a76d7c6812030938fa3f58bc0b0563dea8a1a9cb707745dd93eacb2a875ed8ae2f0b0417a830dd836dc588f0a7fdd7e3703b5cc801e9063f0e414aa35f2a0707640b62f4d0754e2e8b7078cf0df9841af564b81681493c4b6c84c12c15030b6552680f7147d8b64ee1cd8bfcb59cf87ca6b01a4ac20d5bd5d6be03640d4e4867302158bbde7abfc4ce7d8185cc3b4c70b55ff4f282fa079594d4d64bf4ecb20c9b94a260977f27527a41ded656deb591732ef4b4613 +frobenius3|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|-|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e0a0ed2ba7384c5672215cae62d9cb3e516f4176371936e0faa1de4546827180f1f5f798c803c4690769e3a793c95a55735424c5480b895e2996ea8eea0d0eb2da5ab91cb51b613660f9511d3933bd533eb31339df552d0883059a438f6c4a818d8f4868b63b37437177aed29b132f504038b096fd710db141fea968796cd3d1367c4ad4faadaa551eb26ba8acf158557e7f9de44a3b411a31be588434a920d2a084cdd1c71534d3814bbba967d5025d42ec7c920539af35b182e03190cfccd19c6001af1bcc0883885fc766bd3b4bb0f4ddc8d5fa3928a4939b7357101ac612afdcc43c61b2cc9b8056ad06d00b666f1c2e4a91d6b2ab728dafaa89d28041a185881d19842c752a8e80a48a0f8d3ce67514d77b4abdd83ad4ada7554a763be0924aa4aeb5f1be4bd85fec932cc94617730025709ceef42d410955fd7b4a8360339217f3d73f858eb35e601228a847d1c4e02ca22a5feaade710c04fcec538312 +cyclotomic_square|f3632c85cda64d78712a3acbcc4f4592058be24ac854ebdd912414a5abf6932e3defaa1da3075bd46ab5a78163cecdb14664691e45b2e1a87f824d8c0a274c2196db93fc077e9e93bba128d1a62c4af8dd0373180bb1e23e7f8d98dd1a57832660a1bab0242157fde0334da39bf4e4f535079b11264c104d487765448fcd662bee6a03536404085c48b108b08a07f6ab6f3781a63fb630389c095a38c00e2c004627d08b2574cf00be85373ceed2aaaf8a9759d091ce640db489cd6542483e00c8f77d06d00dd547f7535a581132d78e72cbc9eb128a425eae5a389e6293e128cce6ba6b312ed4bcff37da05685046cfe6fd946d2c371a65801ef14e92d42616b7998cf301e2ea49ec59fb27db3a3490097698c9aeb95fd8905e828a27cdfb1bb438c60fca795feb5c141c4211736c1ad2f39264de85845e5a18677571aab70fea98daaac6bad316f98217ee5f4efd3adb39ccf3261b9e0f5f50b44d1e016210e4775a915a479f4d689788afc731d2fb31824b190e3186e55f36c229e167fd13|-|183cb85134dcea88ea916f1fc822aad87f29b2adfe71a52799a814b5996b0c109ece7df9ad89d541eca273a67964f1dcaf359ff19f4791f536597d0605bec31206dcb90ddf60bbde1e1c8f0d44234b0c55de37e46794b63064bf831dc50b5525eb1b62b93cf21643dce667da71b45da8836bd1329fbe8bc6c4fe95d6134be9147d9542cd9a8dd7129938d724b01c50fe216841fb359b95e74d7242c309a12c2cd87b30fe1d355ea7eb5906ffe9574c0eed1f9957190d185f0894ff50da913410dcf7c236222e36f296337ffff8a4127a29a05eb9c9cb5da4efc3b803b29ae22a6511d16065e5e18b56ef94b73f9f4bbe1640a1ef116d48e229d19f6269e9e7152688fa8bea615fc0526119791c1d9b8b74185ecebeec0904b802077e7e4148005dc2795171b683da9f9dfd9d52b7e3907f71be83fad5e1a4d6078493a1f8e7265bff90de8c555a325cefb5e74950eedc644a430147b333495e3a2a33a4e5be1c09d9337caa6cf235a1fdcebc13a71fce172bf6a4f2967df7c9377f0d6072380b +add|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|c15a50a4bb7f8e52df552cc17f2b035c0b07c7663da0b48f3cb414592a9283085aba5ce90889281423bb5e9f20e391ebcf9d807e324010192ae18c69f6743430b2ffa7d2ac53b18b00b2eb7a39a0d32afedcecb2fdd418757554094a5994761262a0b4d67f62336f232798e75385782c61b24e840c5da0ebb64f64092bbd26073674c9137a403d34e011d47231d03b53d49b71fb67d96574dbddf826d2fbcf077e02f3f7133184b60051e356171ee7cecb9e2b52694eb0c334cf4ac45c842c19b8386fb5c6008a8bae616fede48bfc8bb64e0ce5ffc43c170a7c37c63818590b3479d41b038770fe052a341930838e5f416ed50b750c67b068908ad06b9286180a0c5b29062ce2b14797a9b77f401f2d6264cecb920fa058d48ae2285c337222e33c7bf3b1fef4461205aca12b73079d9c16cd36d2e95c92f978aa1d6be7f420c9f52696bdf6fc808b6b8d40f1c3f157cfac740b9cd278d3497cfcc0ecca5421bfee1c8904d723abcfbd67b2e5cfbc73a52ff0d60eb10fba9b60233c060efe10|258025def39c208b498c438d5b2b0efed78ecc60cd41479a340ccee19de5a826249e40a09509f01f071371470bd9bfafab3491d7e09ebfd0fdaede5be0edb50017b4cca20ffb26474c08501d486720f22d618ae91f91f610ffd4f14b2e30010607656d791ff2fc0695e91bbec270880deb4af2e618ba33749f2bfde18561a41e70ccb40b200bb36e502d4f1cb10f250b834b132e0bee93ac3331dd1c968f5804807153933023bfc19407556508c51d1a05105df4bf789ec7d1300907fd79651d2823e3fbcf830f4bb96dd548382d6cd6ac908fab82c2927864d300e1de437620f96c5f3afbcdaf6e61931daf27357838e0161b68daa9624217a24a15884131145e4006406847f4c84025f2c2900f920afa96d996010281644484a12751f09002be2d793863c872377b521bb84a2f5955d1829b518f848417db1b2a2780a2ac2fc94ee5113e8d5e3e6b21a78ab2252cba011c878ae3cb12eb442a13ba47fce32a286179733cb5c180594dc21263d5fdf83aa34462cf19d4c871da7a9346205e26 +sub|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|c15a50a4bb7f8e52df552cc17f2b035c0b07c7663da0b48f3cb414592a9283085aba5ce90889281423bb5e9f20e391ebcf9d807e324010192ae18c69f6743430b2ffa7d2ac53b18b00b2eb7a39a0d32afedcecb2fdd418757554094a5994761262a0b4d67f62336f232798e75385782c61b24e840c5da0ebb64f64092bbd26073674c9137a403d34e011d47231d03b53d49b71fb67d96574dbddf826d2fbcf077e02f3f7133184b60051e356171ee7cecb9e2b52694eb0c334cf4ac45c842c19b8386fb5c6008a8bae616fede48bfc8bb64e0ce5ffc43c170a7c37c63818590b3479d41b038770fe052a341930838e5f416ed50b750c67b068908ad06b9286180a0c5b29062ce2b14797a9b77f401f2d6264cecb920fa058d48ae2285c337222e33c7bf3b1fef4461205aca12b73079d9c16cd36d2e95c92f978aa1d6be7f420c9f52696bdf6fc808b6b8d40f1c3f157cfac740b9cd278d3497cfcc0ecca5421bfee1c8904d723abcfbd67b2e5cfbc73a52ff0d60eb10fba9b60233c060efe10|a3ca84957c9d03e68ae0ea0a5cd40746c1803e935201de7abba3a42f49c1a115fe23817eb10fe06fdb3197d9ece79e07c7a992dde8a93f0ffd2c284bd9a01501fab1f9d5ccdfe46bd86eea8f6691fa338fff3105db2c15df3dcc1099ee557811432404cc1f2d96284e9bebee1a6697b428e654defffff29c318c34cf2fe756104be19ebc421659421dd4189fdfd92efc376cb1b8f180187ca6151db064e61c25cb69ea7b1f4dd790203000206bf3d013cb2a87d1a3218ef89132a55fb6bf701bb8b104914282fb335caaf66d6e1573be3ff376e18238194a50db91546d13c409d87733db0b4cefade20927e55899dc10bb92f1d1a6d6e4996f216755236b8813d8224a9e890771ddcb8b8224b46356dff07e3f02496ee123efae3f987e26751e3fb1ff291657a9e5e31235dd84b3cbb2f5ad8265a1f61aab11ca06cd1c22271e7e6014bed92b8578e114fe716108caa1c01a1ff5616c71fcdad14b19e1b49e18aa833f6133077a2abad1f2ad97358411f04364b4b1b7b4543a19341b3a046204 +mul|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|c15a50a4bb7f8e52df552cc17f2b035c0b07c7663da0b48f3cb414592a9283085aba5ce90889281423bb5e9f20e391ebcf9d807e324010192ae18c69f6743430b2ffa7d2ac53b18b00b2eb7a39a0d32afedcecb2fdd418757554094a5994761262a0b4d67f62336f232798e75385782c61b24e840c5da0ebb64f64092bbd26073674c9137a403d34e011d47231d03b53d49b71fb67d96574dbddf826d2fbcf077e02f3f7133184b60051e356171ee7cecb9e2b52694eb0c334cf4ac45c842c19b8386fb5c6008a8bae616fede48bfc8bb64e0ce5ffc43c170a7c37c63818590b3479d41b038770fe052a341930838e5f416ed50b750c67b068908ad06b9286180a0c5b29062ce2b14797a9b77f401f2d6264cecb920fa058d48ae2285c337222e33c7bf3b1fef4461205aca12b73079d9c16cd36d2e95c92f978aa1d6be7f420c9f52696bdf6fc808b6b8d40f1c3f157cfac740b9cd278d3497cfcc0ecca5421bfee1c8904d723abcfbd67b2e5cfbc73a52ff0d60eb10fba9b60233c060efe10|f4bc26a0631fbf6df051e46143abf9f4da5f6ce3099fc61de40d90f26bf0491502deb43e697e5fbc416ae459a29141d1048e75ce726f1359db28ee92767fb7011b6083ef21b3f587f017d9b4ef4949d4415ad47011241105fa70b730cf25bb27b1ca56e881de83a670486eac729a9a1ff8bead2ee0d203fc269236b69176f92d127d6e7ead966d6e0265ab7063f206e20a6a294c98f891c9013f0a8f145f2121844a42c53a94e23cf775d6ef6d67bb40aedc54d650a090ff3b0a7ab6fd31e318cc369e0e76b11dfc60529ddbe7c85b3ee1cc4618c6bfc9f22922f155264c35273e6877553b6111aadf32184117e3a3cba4f7af4802352e795164fb29ff13320bb47d0cdb48a0a4a0cf8e76098df2195a68dec277dc35cd994684057fb02d49117e2a2adfb5eca1414da3e3b448037466f19abb84f98f7cf3f49926e1583a052870d4c68b599660b54ac44b3c8d92f4aa77bd2354ad71fa60a6f52ebc6c3ca30c0cb8e36538f2e3873491b44766edcc3e6462c996d614418f7d939bbe628e6b21 +square|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|-|793bf0a6010f5d79443afa16c3443292105af47c82b1f465caa4520679105b13d2671c0dab9c8afa0e15f7af3ced4b8897822b3488a241e564122b1998432c2e24d7d3c14cabcf23b653f0841abe02e3ad15669fe465ff0cfbf49dba0b833300d00629aa6467b8288057d74d8735d18440610b53558d5136008cad6594cbab27f82bfa32d0299447401a2c95407ed6daebe4b40350c0b6305f4230a1b7fc3a0c67043f3f7f5a2cf757b4f5320da7ff861c5d45d52c98423bd6e853954364c92a754a784708f7ac70811e6b2efc351fc5c8ffd60dcf0e53717f88344fe71ea019e4682c4545df773db7d93edd64242c30bf6f1284e4fe03524d2f418b9c1171101e591d8edf2aa76570d7085948d2deaccccc18cc1b96f054c3e185a230b28f15d0b503fe678fa928d0d81c090685d70667826250c9adbd8e31e9647a9687fa2d0c8745dbf51e1ba8dcc7b4bc478e766f04890487b8f3489c3881dc5b3d0aac2cebcda7971998e8d384a48d1626556590406df72df0b61b0631e83020999c9211 +inv|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|-|d714a8a347f9335d67e5d36eaacfe9eb4d88fe68ccfab286b4355381eccf4018891be7b40245686104627b96105c47761c54dbdd77b3809b669c97188f913605b53d7c29c3a252be94dd47a9662832015708824e567681768ad730d2fa87ba1e56f138ccb80945bf0218cfcb94a65b0f3482e8f0312c9306ffdc2afdbe058f2715b9b9d3e211da47ba1487003599f86f41abe6c3c68f67caedcfd3898bf2d52da7fbeae5bdb5a89452e2887c66b1381fa196826aaf5c661a1b54cd64d815c42307d3957ca6d62b7e9f8d20300f08ae4c161053f85677e50434ebd589bd24c20de73fb16aab1e002a76d031cd56f4554895f74335cdf6534730bb7afdef69ab2a2c1b81295b84f083fc8ca559c53ff1f9a2f9d57d881a8a8c7d2be107f34f98112ddc1c2e539f171b720482a44e03b4cdcee6f70f78b0197b7240d6f6653f310dde5acea7a54949054a45e58bfce1771059d4ec7847f7f33b09e4544a68129905c70d9e4390041cbe8f14c262c67574661b222ae0b9074fd3dc76248c1bafb412 +conjugate|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|-|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f53804d71209920d099b7c82be0b0d3ec9114d6716feba3348fa56cf4868c6cc22471b3b0c75e107b9c08fa496166a084e16276157baa39a62046e51ee3fbbe3505504accb54e99de4ede80672b7f4ee308d2268cdf434910d1ff48f0641010b43e11f6c0c7f9365c2a24b247d025272ae2fdf28ecb266f9aa283348fdb1d75d93ac2147a4be5c96f5be7ead14581ed00847352be96e026f4cb6a02ef21ae8171dd526de8a20eedead8266033b170814654012c8e42cf6f5dc8ba95326da89323c041b +frobenius|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|-|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e361c1c49737f38f41ba8ed57150ad23b2469efa651a150482c32ae0d16877e2f7ec646f8128b2d84aee975f91ffdf6e8b98acbe68ed5bbf562dfa08176bcad05587e453c47ae538158b2aa3a52a147c934cf43f04fead7a3e9f7da250edb690d93812d5b6910938e50c347d40b76aac01e3bf42d94ed8adebe5e3975eb310c2b2dde5788354b9a4145c36ce6e910ce1f22c92f73e79c853d68fa5cfd16ca351c52e06a71bd0af28ce7b2a07e658f39119d5c4dbf2298b34aac99e957daadd21d6259531b209a4550924d9302a8c6559fb10fe358754c1e2f01c9894e954aff2100fd023dfb635c903da3ebe6f8cc5e16b31b6e479b10979e20f82683b4cb8900d6346639c67089a656eba6563b1757af30483b621fb692f050d786355d14b017b39b7c37f47368bab8c46c63f22ef8da811771c031ca098ae62c0accc80ac22f0f05be1bc1b74b11dd6f4ef9eebe5f1834def6994bf76054f15a87aad9555712 +frobenius2|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|-|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500142af00ee44269fc6d484dcd0dc19d5ec3b8b2e1a83a4f6200b714a7fc07f624d935973bcf20ec05a8f16a31b5bf0adaf7a379755d17dd1d2760bcbd79016612df52ea876537bede001e67ef33cd43d0b4a8b47b37df89c7bd704165c41f3611d27e1f194ede2b0282d14f0c835671b085768715b5cc412bef240d532bb62013a0b4a8946700f09b932395ea742a8959bf1ad21253c4cd19cdb1320c4cc99e1bece6860efdbd1a1366ac20921a0050181fdbce3a13c077b04070ba92b6e44c16accb54e99de4ede80672b7f4ee308d2268cdf434910d1ff48f0641010b43e11f6c0c7f9365c2a24b247d025272ae2fdf28ecb266f9aa283348fdb1d75d93ac2120e303f1b0e501f45e4aee820865160deb3c3f15d26ec5b1e5e7d788e8ca7a03e32440dd7e82485c4559211b9ea97ebb05d2dfda083a491b41833a1ff5c5a724 +frobenius3|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|-|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e361c1c49737f38f41ba8ed57150ad23b2469efa651a150482c32ae0d16877e2fc4471c5df8bc60f52e11547e57804c72d25a84e4fb0852a68cfa06a4253396190c811ec31ec7d8c53abf35387cd66fe0092d128a891b24afb9ddfcc5687b292f1c6c8c51fb118d8ea7cbfe59dbce1b56dfeb5020a15cd8c53414cb70a267bc2fe59f5424f2db0254e3e069a7effb50eb2b38e179d792c907541047b536835f1b39d62c76db9f12d8456471ee0ea6ef4edd21743c765a01cd114a362266963628368a5c9e5bf39ff4b0efed3ba239faf7db0d0bed7e1d6eb53e5e22345655702a47007a9b1b28c4ab4f278681989d2281aa3c133a1b35b91909a80a5ebe82da2f71c8169f501b979536dfca1156532ae82c10461f978fbdc7d8c8aaab153ab4183948df07be58e6142b5d3fa9a820d7f7134a7d879b095bacece9af9cfa665b1f22f70d21466c473e989ab23a5794605bf689f102fb03ba8d080476bf36efa10a +cyclotomic_square|6425d539381d92386a3617ccdbff0aa2cc8705fa8fa1920af857b9887353251e11e1608fa30ce847712284107c60af5b39ef91da64a4ff6ffd6d83d35cc7e500acb1a1a8793396f7d820d60aa031ce5e8ddc1eb8d8012e54b3201ae347eaee23a5c4b8a29f8fc99771c283d66eeb0fe18998a3620c5d9388e8db98d85aa47d17815568d0bc569676fde5ec1111aa6a4f0c0823b4595a7ef081f315d736e2ec2c026f609b1cf23a0b94b6710ef1a6364b397131a2562aee039d61be42a0f5380470ea7346098385bf0a0c665b53a16f4af64183c682fd55615a57c91aa62b1d150cf107f70ed35face8335bfe881c6b70fc00c7dd1be34b4ad8b1f1258ffd0e2c9b3128ef78a732538658ba73a239f474f58a8c4c253831c49999f0df670b8310dbf0fd44b1c97df0684d6f161fbc51b8346cce1abd9a2785e1a27f0915bbb70e0059be7b809661bddfb5194ac1613a62326f127f47f99917fbad16f95a318f0969725cea37de9dd5898f5a607d0541859573548bc068c40ed679575740126015|-|11ff824e1a6e30c9c89e014154a49ac3f4f2fdfec09345efe265ecb66f514a02214aff86031878604d585c92a2dcd40712a9f2bf25470d8044667bd3bc09e50aa1f54f93de1aa42ec441cf236fedf6100f0c5c4a539521e44b043b3346f2232a08e8f6ece0b2d6e6884f3e2edf7a5aa7d8e04e5f7dabc4a1f9e7cd2916b2e51c7c894a33eacc03cedd1277abbdf9c0286cd43252592dea9025f8be54a70a7b1d995620fd045e5be0fef30ce46e82f30008b6aefb5037c2faae220ef1389db72a0cc856d82bd6e7a53f2080035995b93bb793c5a1e0aa5c3d8d10a1e9912feb1430436797060cbf9ce948bbf067f0e3bf3c114419898ae3869b7f78ed4fc27b27a9553f135578c1beaaa5e11c49e27b5be1c29324f809b7cf417542412a0ae709c645b0f36f7e7f4071668d59b121208eb2623075e56de5fbf71931b4bf48be1e9b8dfcfe60d47c9979a60b221a3f7222faf6201a168efcc631b97727b331bb05ed71718a653dac593da8e5d4657c9b29f0be715de77093a7946f20e28e976b25 +add|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|28d53b9752a5d86b76d19b4480a7d00b62177a04c1c5cf1ae46253c3ce25251f5b5930fd0ae28c849834663540f91ee819c20b09bfb98755d882b4562ce5542895d93568b8e9760f12c258b9299b4511e8901572f14975755f373f96685d6902792c616d89565c7de23aa311210cba4765b82c053294f4ddc75546c2dfd4d123805dcfa3edb67d3e691284a4d5650e1215de046bca8687af162d7e680ca6ea28838e45164d56f7a71e765a06665a294d6d8de4960821c4a24f39b9f4e543020a589db03276d3f3b021bdb908940309303db560c2dae58d14859b68836fd38b0579a3d2ce0c9dbe8f931c8ca46f7e975255339848e4dc296ee5db32530c333011e454c275be69083e1fa00773f96ed7a82f57bc92b90b4e6bb5f2c8d67985ec14ea25a1ff3bc0a47dadc64e36b5c4f5115308a39608617fd584a1cb2eadbd4c1c9ae4f294df75a89c22f0fc24b71bce592dcb4132c39a9e34b2e339a54260ea1a0978be5aebab93a4d5c818c141fe443577402810164eb24eab0cdc5fc2c45521|828f9720412ee93c7295d286f09206f19f312060a2ba1478b1451eba491e57271f1ea53c88418d4996a29a219078bf0b8e89d14fa7bd61f1b58a1aad2ffb0d284c4258957c89741d1c0249ad9ec672ca4086658dcff14b5580c26eebf7e368262c1d00ee4dc7b5072f1db812b88680c7a84971417c0e030c0669f96c6a5f780f791d4f7dccf1e84aa97f157d2bc739688a06bad1e322aa6a5623631a24d36a29d98480fd61dbc2468a2248d090151f995db5dd996019480f3198caaedd802b23a1e81fc5bd27fa5a31abaaaac56b3e9d26efacff7ca5223265b71ad26f92a32b68d7d5c59766f814ffa9719a1d1dbbfa6b78cd070a0d38c87335c6f1102b4f18c78c65d18e689adfd967042c113ff78b738b3a7cd90d7703802add642ff0c62706e78ef63147a60a96f9d3d69058f8ae28bb9d697458aa1d5fa4e076fb71a62f8e22fc2e86526f46cfda600cb0382518fa12110aeeafdb8af22e760cc5d8a90a929576dd20eb50fcb9107b93471af7f805033ad9a4facaeb9f07d7e821a44129 +sub|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|28d53b9752a5d86b76d19b4480a7d00b62177a04c1c5cf1ae46253c3ce25251f5b5930fd0ae28c849834663540f91ee819c20b09bfb98755d882b4562ce5542895d93568b8e9760f12c258b9299b4511e8901572f14975755f373f96685d6902792c616d89565c7de23aa311210cba4765b82c053294f4ddc75546c2dfd4d123805dcfa3edb67d3e691284a4d5650e1215de046bca8687af162d7e680ca6ea28838e45164d56f7a71e765a06665a294d6d8de4960821c4a24f39b9f4e543020a589db03276d3f3b021bdb908940309303db560c2dae58d14859b68836fd38b0579a3d2ce0c9dbe8f931c8ca46f7e975255339848e4dc296ee5db32530c333011e454c275be69083e1fa00773f96ed7a82f57bc92b90b4e6bb5f2c8d67985ec14ea25a1ff3bc0a47dadc64e36b5c4f5115308a39608617fd584a1cb2eadbd4c1c9ae4f294df75a89c22f0fc24b71bce592dcb4132c39a9e34b2e339a54260ea1a0978be5aebab93a4d5c818c141fe443577402810164eb24eab0cdc5fc2c45521|79e29ccab26f58a112bd0c6681aee670395badd8d674c5fa1220a9141f217119b068c11a8909947cf203401fa1f002d3b75d3bbfdf8fa2fe2e25e3e0497fc807228fecc40bb686fef77d973a4b90e7a770643aa9ec5d616ac153f0be26299621c8be37c468323e85843c55c098430f6799891a3a8571bac0c9fdcfaa90529d28c05f2d0e08100e0a64257f9c11669edbbda2317d055bebc35269982a7ed5f907d367f5d0c72ed4f64c3693c3c460ccfe829a146c4fd7bfc9912558c511f9260ff1adbe5fd18012f9ed3037999d642c3dac84eb7ac7d906095b8049cb90eb8b20bd8dad0095b89b31653bcbb9cf8a0ded1e6a1ef8f79834a4d21d922c6b13532646e05dbe2821aa9f28f266aeafcbc9d1713543d81c3c2be53ee57c98ae33522e7998c9cfd0527d4bc836a8d2b7398e22e002d9bd19dcfb2a7f017bfa13457127a15693ddddf23e4917c5d82ad36b0afcfcd40e271ec0eed9b70734a3b2663905c7a27600611f4aef9b49bb795588ee2575da6a3a2fa4b606738e500a1069fa16 +mul|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|28d53b9752a5d86b76d19b4480a7d00b62177a04c1c5cf1ae46253c3ce25251f5b5930fd0ae28c849834663540f91ee819c20b09bfb98755d882b4562ce5542895d93568b8e9760f12c258b9299b4511e8901572f14975755f373f96685d6902792c616d89565c7de23aa311210cba4765b82c053294f4ddc75546c2dfd4d123805dcfa3edb67d3e691284a4d5650e1215de046bca8687af162d7e680ca6ea28838e45164d56f7a71e765a06665a294d6d8de4960821c4a24f39b9f4e543020a589db03276d3f3b021bdb908940309303db560c2dae58d14859b68836fd38b0579a3d2ce0c9dbe8f931c8ca46f7e975255339848e4dc296ee5db32530c333011e454c275be69083e1fa00773f96ed7a82f57bc92b90b4e6bb5f2c8d67985ec14ea25a1ff3bc0a47dadc64e36b5c4f5115308a39608617fd584a1cb2eadbd4c1c9ae4f294df75a89c22f0fc24b71bce592dcb4132c39a9e34b2e339a54260ea1a0978be5aebab93a4d5c818c141fe443577402810164eb24eab0cdc5fc2c45521|0f085170c07c8a32e14697f402806e151dbeabd1c3db7e0a0bee7574cb32b52792e5bb1cf889db0c7a5598074962675aae08f48ea67c852396f1be366ce052041abbc59065e07b1fc36f156af4d5d976f8e0c8326ef20034a204fe5b76ceda169c68207323cb251317a9230c15082e2198094e43b242a603aad131a7a309ff0403d2ca18b50a5234fadbe02c6d457afc54dfb6dcd48f42095c8e5f8a4062bf2ff21ec2a01412db1adb7b0eb9c232e92669004bdbc1ffdc6914b2058f5c6e3f132687410b1ea2b1fb8e61904337a232f91389c06653d9fe40b0434ca67e43ab11e15187b192df4271f0193b9453634fff1c77ec14c843c9b5ad5ee125f9aa5b102ecb238d78041e1a00fab0eeb84bbab0b0a6308fcee3fbe51c5601028375c013bb6340e294d7c4322a52f35394644ddfd842727b97650cb93b642422592e4b2a9ef2ff3c2d9dba857d3d817b54e8612fa9f328261898dde63eca521455d0c10e5300ad4d2f61c695c48c8961a4699b04c111e5bcbd3391e9c386336639449828 +square|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|-|4e6ae1f7d12c7bb6c552fba793e9202bf969ece0cd671370cd046718d79d6205a1fb63d50670eae2d1e3916c054b31e276e3f4404cc049113ebd7aa3f7ad19121e7cfab0e70a5696a87ebd295095e9d5259c0bc5c28d0ccc5ac9ac614ccdcb05cd0ead34377fa8b6cd43b95acab39b29002fbc63a6ac8e0534d360f964a17305040a3f9f5e78df565f734358bb58969370edef244fccdf9b07770abf4452bb28defb742531e7c9f85255aaada825185db62d3df24994065555a558b5d370a324ea1df67f9ace10b875c70459fe52437f31630035f278e8d531d9ac2037920d0d771331baf13653ca3f1fbd0903250ee11820b0d34299373694d5f4e28161600b29cb5333fbee37e73ba7396d78223cf567cee2a3479c94f1c5d842952a136b259513483333897015fd3fa4ad39fe428005706690207bfbfb0127ce9ca3cb061a703f8c12dac14d2b625d64fc7e7ce52a5f9ce4095b9fe1282cc231b927f0bf137ddb18126c5b13337098e5416e279080705193cbd4b22af7fcfb9aa127337122 +inv|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|-|3f7082cec8189e572770b6ce14f49aaa29660b98eaa7e1cc7859866285c07617b28e3a24a23a268b662297076d845663038543c406a4fbf646935877b61fd22ffc4501e058e986994056baa5120aa30ed19c5faac098870f802d4eed37330f1a728efbeaf78502e8a881a68ae715270dbc3ba587ec87010a87d70599a9db7b19cfed0b5ed2c04ecb1e7855b7ea473bca1d9f2e2ec1d3f5683341c4e0ac6a5a0e528d03448a04bbcae1cfbf0ed9f657526e7f6dd99ea71cfdba0f55ce206f9d2cef608eecc48295f37688011b1691539bbda8ddb15f698fa45bdef4fd2bde0b289e8cec96464a44ef22dc69c1ab578cbb100cde4546c6628ccb799067edc0ff17928c4ec82c44f88b13649fbe4c2a31940fa47b53948f573050b9d63ac552e32478dce9d4b7f43e6bd0a33911b58e253250924f9cf0d8abc3bcb0592f56258f0358d93361333d4eb01cb37446566d1a791be5469074b9b3077b8ddc65d590a916528a8ab9e0029b744fd4ec3db5b079898d84c8414c568d6f9854b1a871d81a2a +conjugate|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|-|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919feb10d46cf371a927ddc80c65f024c2a741e35441486bb9a49847f92728f4c0a58c979e18bc2e6b6213d8c72e3cb5def46134cc29015425e9b469e426e56452964c5d97c468d8e9ad20275af799a61b419240398964327205f681d53bde3891d2b3c8fe120051fafa497ecc7b5d67efa87a586ae4a4e25704f9d1c99249a0a1d0cc2f6655923395653159c1807e3a84133b83028d5eac2a9bfb4c3987d874010bedfc455e14c63e4a8820f968b4ecfd3ce956fb82799371b35a53658136f7828 +frobenius|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|-|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831083c3b8bc082a0ff3a0292cb13b0805fdc8b383ab917fc256422f899a9fce9460087d7c49f5e8efcc79fd8e8a8e4d40eeba20c81e6bf72d0b19822b149a80dbb1760bf49cf29416156ce4bdc13226f8f8bc8769340733b840711bddcbb4dc4222708c8c1ff4a2c137457d7ec2fb077cd8d14b6da2e65f73d6dce02c65ba39b3c2e77316b8c412f6c583b3771dfdbbce588c570ddc90053564d66b3b5eea27fab27849b72443d932a77022607150d28ec5901e6bc37ad254b361f014d086f39b50703e82aae5e0852e02855d1e75ea0266845265d955f21e2c982629382929208160895b6a29131e428f63b09557a532e12665bd1e6db7b79a66f5a8512a810c51c2ff6468779109889b53e8ece8d9da961756ff400be994c09c6c2db0ec1db7601da853b390eacc5caedbca2c42747c7fdeb76c5e879c45d642d0f4ac4706f3b25fbe87d26698cd799d55a8c7fc6ccaf02e1d6c8e90ca7fd0962bb4e10410c240e +frobenius2|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|-|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d302ecd7b418cd648ac7d1ee4b0933365fcb2f4b55b0ff54057ce7d6861cb9b5b06af68faeef068d299641de271489faafa55590a5ed4f6c918d81f5b605a44ae0ac2385f0105c1e6d4f48c7026c7c8df54ec8753f7ff9cc317ba91f8bbcbbb262aec34a9991a34562f7c632f2ab6ab0930aee8ebe2eb28430fe72439fc135aef22b43716c66369575d19ec9ba3c764e66791952fb6d7d1718bc68d12526f73422b97a8538f1a17355a0dfb76f7ffafcb516ce6770c90b5110c50755fd209f9520564c5d97c468d8e9ad20275af799a61b419240398964327205f681d53bde3891d2b3c8fe120051fafa497ecc7b5d67efa87a586ae4a4e25704f9d1c99249a0a1dadd5cc1900ac7040f5b71862fe232d0d0dcf47b514f1fc342da1148d5c71f2222c9003970249af9a4dd0e1f9a600c48d85998885f9aa16c69717cc3373e0d906 +frobenius3|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|-|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831083c3b8bc082a0ff3a0292cb13b0805fdc8b383ab917fc256422f899a9fce9460092ee58f49d356315f3df55c11786fb1276c3b3867c16f163c462aa73d02e240b4dfcc27ed04f5753aa54e055312d7b14179b8edc708b68661507c68145df4f11b4589614702397c664e43f2f695185c89c62daac429802b9e9926af0999acf02e5e01c88dffc50a17cbccef1bc984ae170605c301bd1f644f821d4bbbfd8c00d1126075d63cd4af5c5ca61c1298c76fc1dfd329876de96c2b7c54f5054d85603f7d32680d84ce5076e13ca1a023b6a4d1cf2b1ed72069b5d65c3eab1badd280d3f68c635855a3c13978e681317175385f7fcaf9adac9d611ba45acceca3d9f13180736519d7b88b2d78be39903cdd735e8e88c80f8ab03af63dd55d2b172ed2e3a9db2664f36479ee7ed65186bbc35d87dd3f0143981c22cc1693db13a289729976552d180691f2e31441ceb7bbc7e28cd91255f7838efaa50bc01e233629b10 +cyclotomic_square|5aba5b89ee8810d1fbc3364270eb35e53d1aa65be1f4445dcde2caf67af831080bc2f11794eb20018b38a654e1e921bbd11f47c89e492a5407a8973776641d30b768222dc49ffd0d0a40f0f3742b2db958f54f1bdea7d6df208b2f558f86ff23faed1b59dbfc79c6d9ac866928e54717a1e9c5bd00c05ee667b3e48bfdd80a1cf9bf7fd9de3a6b0c406d91d855612b567528b566199c22bb3ff6e4b1172d800056f63ae71485cb9e6bacedc92abbf54bf027f90258f8836ce15e11baf73c2919494b6f92475406aa0feef0a13168356de9394c3da2bf941de01bb24e00bf1726ef3303f78ac939856b8de5f5ad9e23a8164535bf25300e5a8e59939e04f81e07e337a35bd0fe91a1bac7fcb817d01fe343347ee91f022998ca37148eb56ada121cc1edf6f586018de83285a0db93029dd5b2fad26bf72a48da0215484eb459133b3b8672bd68e7e539b5d54f8a87d8552aa05059e15a8d0e6aeb6d48f5c62320891db882353fbd57e44762d2051cb2c38ec211c98eac189df4fafa885fdfeb07|-|04885c4e123ab4f2319dc3d43680bae92255dd25cee0c09a826596e530618601be69b3334946656f8a7d7843803d12e7481a06d265a4e2875a945a1706239c136f011fbea559c33930e27ebf975210acc5fc62c4ab498c604f7e88440938db119c26424c98a382bc0921714d7e49e0ba5f44fe73616bd3842bccf70c2662fe02011f6bbdd4bbf9385aa83583605b44d77f4786ff4f16423086889a745364371220a0f157c7988e3d5b87c651a874b7087d095999abd61d912794936d21877813c52d878b624527491ac3d7c810fc69f20e7051162130c08e473444813bc0a7035767a8893dce85f62e6b612ca2cbbbf11667d39802e2d3dea6976fd0550770142b1fa787cd46f69f5cca5495f3ee9f038c2916d03e9398bc38edc591aa65471e40a1c4a772c497061096eb2b419feffa9bdc41650362022110a0f4b14b81410667e96c6c0e1c73381a15ad147940a3833e974abdeb2af26fdffec373877ea01429cd89669f5d6cb5bd58afb90737274fe41967755e5b8657aed06b0c8e957e27 diff --git a/testdata/zolt-arith-diff/extensions/fp2_ops.txt b/testdata/zolt-arith-diff/extensions/fp2_ops.txt new file mode 100644 index 00000000..28f27666 --- /dev/null +++ b/testdata/zolt-arith-diff/extensions/fp2_ops.txt @@ -0,0 +1,48 @@ +# op|a_le_hex|b_le_hex|expected_le_hex +add|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +sub|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64300000000000000000000000000000000000000000000000000000000000000000 +mul|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +square|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +conjugate|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +add|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +sub|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +mul|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +square|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +inv|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +conjugate|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +add|03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|00000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000|03000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000 +sub|03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|00000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000|030000000000000000000000000000000000000000000000000000000000000040fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430 +mul|03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|00000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000|00000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000 +square|03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|09000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +inv|03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|85535390645dc0d20887f69a0b4756ba93e50001cf8335d01bc0cb40f7de42200000000000000000000000000000000000000000000000000000000000000000 +conjugate|03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +add|05000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000|0d000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000|12000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000 +sub|05000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000|0d000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000|3ffd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643041fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430 +mul|05000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000|0d000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000|cdfc7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430e400000000000000000000000000000000000000000000000000000000000000 +square|05000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000|-|e7fc7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64306e00000000000000000000000000000000000000000000000000000000000000 +inv|05000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000|-|ec81c3f156e1ef7fded9760d20e8ea44c65be74e0609c9e045ebaaa2c45e2c1cbadc4bd0d48e64f8301cde4cdc3c31646d80d2bbf8dd7fe8ec6d1ef60166cd22 +conjugate|05000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000|-|05000000000000000000000000000000000000000000000000000000000000003cfd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430 +add|12b21cff4c474eef5bbdb5e5b0c3711a73f56a09794bf20a7cedf200e807850feddaba6aac528dabde8169ab39021c8fa5ee020644d516b3956f5a900f459905|6749992bf55705fad5dfd4190a6a4d670db696e5d4b33016d91e600f65bef905e602d2bacfbb8db9d3bf988f18cbb155e736356741c26063fb052b61c0e0851d|79fbb52a429f53e9319d8affba2dbf8180ab01ef4dff2221550c53104dc67e15d3dd8c257c0e1b65b241023b52cdcde48c25386d85977716917585f1cf251f23 +sub|12b21cff4c474eef5bbdb5e5b0c3711a73f56a09794bf20a7cedf200e807850feddaba6aac528dabde8169ab39021c8fa5ee020644d516b3956f5a900f459905|6749992bf55705fad5dfd4190a6a4d670db696e5d4b33016d91e600f65bef905e602d2bacfbb8db9d3bf988f18cbb155e736356741c26063fb052b61c0e0851d|ab6883d357ef48f585dde0cba65924b3653fd423a497c1f4a2ce92f182498b094ed56588f322202e988c4284b2a1ebd01b104f20b9580608c4096110c2b27718 +mul|12b21cff4c474eef5bbdb5e5b0c3711a73f56a09794bf20a7cedf200e807850feddaba6aac528dabde8169ab39021c8fa5ee020644d516b3956f5a900f459905|6749992bf55705fad5dfd4190a6a4d670db696e5d4b33016d91e600f65bef905e602d2bacfbb8db9d3bf988f18cbb155e736356741c26063fb052b61c0e0851d|6c55ee753ba78042f2611ba2c50f03cefd695fe7a922039548285c19a694442ee19dca1466a713c2644b27f2aca434009bb98b50e8ad38ad2e61dcf861d02803 +square|12b21cff4c474eef5bbdb5e5b0c3711a73f56a09794bf20a7cedf200e807850feddaba6aac528dabde8169ab39021c8fa5ee020644d516b3956f5a900f459905|-|e470828eedf4fe951a0e3f44e1f9af06a5bf62d840dfe6ad9ea02cc5fdcaf10859ed2d2f87e2a0a3bb0813d45a0eafe9e8a8dfab45cfb35de53bc6c84ec78512 +inv|12b21cff4c474eef5bbdb5e5b0c3711a73f56a09794bf20a7cedf200e807850feddaba6aac528dabde8169ab39021c8fa5ee020644d516b3956f5a900f459905|-|65718a617ac8c1e635f62cf2d8413e91ef6bb9c1ad7146dcaf5814abe9000f2b273d8692bcba0ade485f741063ba2cee18ef5368bc6703e8338ea2c983886505 +conjugate|12b21cff4c474eef5bbdb5e5b0c3711a73f56a09794bf20a7cedf200e807850feddaba6aac528dabde8169ab39021c8fa5ee020644d516b3956f5a900f459905|-|12b21cff4c474eef5bbdb5e5b0c3711a73f56a09794bf20a7cedf200e807850f5a22c26d6a399390ae4808bd57686508b8697e7b727039059430d7506309cb2a +add|972e70c57c0cdbadf190d5ca14eec5b3a9d2628c22d199df159388f023ccaf1e10fb234c09fb2b0980a324759a69e3d64969245617d63174c3ea9268251a780a|6fae3d16543a74ff7382862ddd7450b77fe8f75414922b9272bdce3d02bc2314a16fc86f8a52320ef269d9254b9fc02ac86c294a3e4d52fd3cb534acddac0b1b|bfdf3003baba2e71d848ea8f60f894d3cb62d95f801d75b95eb0254db3396f02b16aecbb934d5e17720dfe9ae508a40112d64da05523847100a0c71403c78325 +sub|972e70c57c0cdbadf190d5ca14eec5b3a9d2628c22d199df159388f023ccaf1e10fb234c09fb2b0980a324759a69e3d64969245617d63174c3ea9268251a780a|6fae3d16543a74ff7382862ddd7450b77fe8f75414922b9272bdce3d02bc2314a16fc86f8a52320ef269d9254b9fc02ac86c294a3e4d52fd3cb534acddac0b1b|288032af28d266ae7d0e4f9d377975fc29ea6a370e3f6e4da3d5b9b221108c0ab688d8b495341a371b04bdb7e034a443df547c8d8fce2f2fb0d58f9dbabbd01f +mul|972e70c57c0cdbadf190d5ca14eec5b3a9d2628c22d199df159388f023ccaf1e10fb234c09fb2b0980a324759a69e3d64969245617d63174c3ea9268251a780a|6fae3d16543a74ff7382862ddd7450b77fe8f75414922b9272bdce3d02bc2314a16fc86f8a52320ef269d9254b9fc02ac86c294a3e4d52fd3cb534acddac0b1b|affc850b9721a021a0828c77e4276848f99afd2778947c131344ad3b615e8c013911fd9c475a8cdd7c570fc2aa02fc7325e688f652f04f26a0e43bc199482d25 +square|972e70c57c0cdbadf190d5ca14eec5b3a9d2628c22d199df159388f023ccaf1e10fb234c09fb2b0980a324759a69e3d64969245617d63174c3ea9268251a780a|-|cad3c44e4c9afd0cfe1b56c7931122536cde8b7a79c19d909600171d4ba49010127c56160487129c29b87b3e050113e94add52addb3675436fb6d5b6b2b3760d +inv|972e70c57c0cdbadf190d5ca14eec5b3a9d2628c22d199df159388f023ccaf1e10fb234c09fb2b0980a324759a69e3d64969245617d63174c3ea9268251a780a|-|e6da4f2d20c7aa8793b097f7aac8baf0b37b73cd32be7dc6a7b7306fec43172e287ee9c3ffba3f92b1d2e4325789bb9bf031e51a3d77deb6e2fd45aa25fb972a +conjugate|972e70c57c0cdbadf190d5ca14eec5b3a9d2628c22d199df159388f023ccaf1e10fb234c09fb2b0980a324759a69e3d64969245617d63174c3ea9268251a780a|-|972e70c57c0cdbadf190d5ca14eec5b3a9d2628c22d199df159388f023ccaf1e3702598c0d91f4320d274df3f6009ec013ef5c2b9f6f1e4466b59e784d34ec25 +add|f3b95ceada951aa358a5d73d161756d00083bac1eccbe691fd80969adfa3a9142231b8c6d0fbaa53cd891cd094638d0a12f9b4be7e0211a15953081ce6244317|f62f7feb1497f95dea0a04618072b89ce3cd1475962335e8e14744629bdac908b4d28269f7ea8c49806c73f231693f16a4d4f93e742d4f17ecc27a692703242c|e9e9dbd5ef2c140143b0db9e96890e6de450cf3683ef1b7adfc8dafc7a7e731d8f06be57b15a1761c02b1e5a35624b8958752d7c3cea0f001c7651a49ad90213 +sub|f3b95ceada951aa358a5d73d161756d00083bac1eccbe691fd80969adfa3a9142231b8c6d0fbaa53cd891cd094638d0a12f9b4be7e0211a15953081ce6244317|f62f7feb1497f95dea0a04618072b89ce3cd1475962335e8e14744629bdac908b4d28269f7ea8c49806c73f231693f16a4d4f93e742d4f17ecc27a692703242c|fd89ddfec5fe20456e9ad3dc95a49d331db5a54c56a8b1a91b39523844c9df0bb55bb235f09c3e46dae71a46f464cf8bcb7c3c01c11a12429730bf933170831b +mul|f3b95ceada951aa358a5d73d161756d00083bac1eccbe691fd80969adfa3a9142231b8c6d0fbaa53cd891cd094638d0a12f9b4be7e0211a15953081ce6244317|f62f7feb1497f95dea0a04618072b89ce3cd1475962335e8e14744629bdac908b4d28269f7ea8c49806c73f231693f16a4d4f93e742d4f17ecc27a692703242c|83f8bf3a837282ec698c9665e95ec23ae877c0fce867446e34b2dcb73299631a1f12c9c0f33e65e324c3454ceecc65ab7d36596c107ef414829ede3b5a912d2a +square|f3b95ceada951aa358a5d73d161756d00083bac1eccbe691fd80969adfa3a9142231b8c6d0fbaa53cd891cd094638d0a12f9b4be7e0211a15953081ce6244317|-|ff5de937a62379203d32883e4652e92428bd70db25f734320c160cab972e7804ce5efcf28be466748155c16275e2c098376d2005cfaa9f5ebc029ad68e20142e +inv|f3b95ceada951aa358a5d73d161756d00083bac1eccbe691fd80969adfa3a9142231b8c6d0fbaa53cd891cd094638d0a12f9b4be7e0211a15953081ce6244317|-|6a9733f9f62215e384001dcd79d3b2f43cde1e03937e132209e29ead5d3b4906421c2307931e4e247afb9b2ca06e32ad70ce7cca1f6106fe641615e81d773018 +conjugate|f3b95ceada951aa358a5d73d161756d00083bac1eccbe691fd80969adfa3a9142231b8c6d0fbaa53cd891cd094638d0a12f9b4be7e0211a15953081ce6244317|-|f3b95ceada951aa358a5d73d161756d00083bac1eccbe691fd80969adfa3a91425ccc411469075e8bf405598fc06f48c4b5fccc237433f17d04c29c58c292119 +add|64fa909b5d5584193ff4442ef4e157cd379b6c60d6678950cca0d6b7c22aa9064d9538c2795300f702d35e7e2df523fd94b2c89ada87e1a8b9621d7fe775c70c|d911f98fdbb3b5955a8b259f2fe05f8286337b4f35da1bb73c25eab9cb7b7c226059b335f129b51265c2b7e2567e76c1ecc76819c3a515d05032871bbf4fab24|3d0c8a2b39093aaf997f6acd23c2b74fbecee7af0b42a50709c6c0718ea6252966f16e1f54f194cddacaa4f8f20819272422b032e7e7a6c0e0f472b933770e01 +sub|64fa909b5d5584193ff4442ef4e157cd379b6c60d6678950cca0d6b7c22aa9064d9538c2795300f702d35e7e2df523fd94b2c89ada87e1a8b9621d7fe775c70c|d911f98fdbb3b5955a8b259f2fe05f8286337b4f35da1bb73c25eab9cb7b7c226059b335f129b51265c2b7e2567e76c1ecc76819c3a515d05032871bbf4fab24|d2e514e4982defbf713391f7556c79e20ec0729257d3bd51b91b1edf69fd9014343902659fb56b202bdb180468e12ed30543e102ce271c9192d0c7449b748018 +mul|64fa909b5d5584193ff4442ef4e157cd379b6c60d6678950cca0d6b7c22aa9064d9538c2795300f702d35e7e2df523fd94b2c89ada87e1a8b9621d7fe775c70c|d911f98fdbb3b5955a8b259f2fe05f8286337b4f35da1bb73c25eab9cb7b7c226059b335f129b51265c2b7e2567e76c1ecc76819c3a515d05032871bbf4fab24|62b3ca6e4e647d44de514901958f7ec5e2db9f1caf6409bca439435a6324ce0d06fab3fd93ce47222cc6c186c54536e0589e46882095a920002add44e1736425 +square|64fa909b5d5584193ff4442ef4e157cd379b6c60d6678950cca0d6b7c22aa9064d9538c2795300f702d35e7e2df523fd94b2c89ada87e1a8b9621d7fe775c70c|-|c6e4670efba5a887109439e9acf8c0e1b7c55dbcf9d7e5aa9dfd2d5d5e68602e4b954fa766c84bc9a30d49f49466fcef2d8befc451b990a1da5d3e9a68787900 +inv|64fa909b5d5584193ff4442ef4e157cd379b6c60d6678950cca0d6b7c22aa9064d9538c2795300f702d35e7e2df523fd94b2c89ada87e1a8b9621d7fe775c70c|-|2ef20cbd4fe381017b384fb0500aa07d86e77f8f6158ff300fa941a1e3f02110d9b4685547257a26c048f1b744842030a5e4ad75a40ac5f1deb177066e1d4e27 +conjugate|64fa909b5d5584193ff4442ef4e157cd379b6c60d6678950cca0d6b7c22aa9064d9538c2795300f702d35e7e2df523fd94b2c89ada87e1a8b9621d7fe775c70c|-|64fa909b5d5584193ff4442ef4e157cd379b6c60d6678950cca0d6b7c22aa906fa6744169d3820458af712ea63755d9ac8a5b8e6dbbd6e0f703d14628bd89c23 diff --git a/testdata/zolt-arith-diff/extensions/fp6_ops.txt b/testdata/zolt-arith-diff/extensions/fp6_ops.txt new file mode 100644 index 00000000..5db77550 --- /dev/null +++ b/testdata/zolt-arith-diff/extensions/fp6_ops.txt @@ -0,0 +1,40 @@ +# op|a_le_hex|b_le_hex|expected_le_hex +add|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +sub|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +mul|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +square|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +add|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +sub|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +mul|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +square|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +inv|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|-|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +add|010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000|0700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|08000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000 +sub|010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000|0700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|41fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643041fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643041fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643041fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643041fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643041fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430 +mul|010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000|0700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000|78fb7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e643070060000000000000000000000000000000000000000000000000000000000001afc7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430ad0400000000000000000000000000000000000000000000000000000000000020fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430b600000000000000000000000000000000000000000000000000000000000000 +square|010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000|-|56fc7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64309e020000000000000000000000000000000000000000000000000000000000009efc7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430250200000000000000000000000000000000000000000000000000000000000032fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e64303800000000000000000000000000000000000000000000000000000000000000 +inv|010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000|-|ba55faf8d3f32fd7be2487f4c8f59c396d03c26d82343f4c5c6ce82d8aa3862c52eb5d2aa07c3c9877f7ee8791b561573fd922119318534a93948a647b73330b16617344321e93946654750c9384e2a627c005037f59e78b0d397e911747e60b837e2183dd12af585a5913219a2a5132e226940d11a0a22d83a9f96f884b8209caa67d2610acbe30e00999663e3ac29d1e5874ddbd4b835c0bd8c791285d3c0314ff830a33bdff5abefbe8e6f769f4d6fba295b5d1df2bcc969f21271bf2c41d +add|8310f51b9d5d7a142188951ae7c506c00d260ae23ce974ec226dbf72e85cc62ceb9e92b0b54e67af29c37a9e9545698b55559f050a7fa2fea1173a75632b4209a04fc68f0299a4e2bdb084fbd0ebc416df5d273579eb0f31352d59db9735c61bffdf73ed7cc502c9c52bf47caa93ddae6736af16bcbad8951130f235648f060f5d2f908003a3a86baed13af94aa0b218020a1168204a6fb59612f3e0730ef70eb08694e952bf698fca80403263b9f5a173d9385e790505b92d366657ab077905|c4bac3714f1ecadf995e7fe3d143541633e9d2eafdd91913b94ea985dea2b12debca3458bbe24dec42074a20bcac337117bca3a9872d88b581b44921ca0abd20a81103847bc99222f725aa2bdb75ad9b3ee93b25504e0d78992077a454643b1300c5297c0e0e7a7f09c71fd3ace8c1ad2a7449e598f52c9d633569e9338ec01dad4040c021d349764e6fc73433ac43523e2b3a3d3d998b570f2590f7e2f38b28f565e970e5d5d454a2cbf8183cd354bc790cfa66d081e1af99e7d9d72ebe5c12|00ce3bb5d5ef23b82d1ca395279fd93ee3b65b4b847d3e47b21b371754b1132ad669c7087131b59b6ccac4be51f29cfc6c1143af91ac2ab423cc83962d36ff294861c9137e623705b5d62e27ac6172b21d47635ac9391da9ce4dd07fec99012fffa49d698bd37c48cff21350577c9f5c92aaf8fb54b0053375655b1f981dc72cc37253680eead1a56f7690c5ece174d3e2dcc923a79daa547c9751f7e3b31e07a5ec7d5a38953ee46c4c394b9f8c4a5eede532c54987e668c71d402fdac5d517 +sub|8310f51b9d5d7a142188951ae7c506c00d260ae23ce974ec226dbf72e85cc62ceb9e92b0b54e67af29c37a9e9545698b55559f050a7fa2fea1173a75632b4209a04fc68f0299a4e2bdb084fbd0ebc416df5d273579eb0f31352d59db9735c61bffdf73ed7cc502c9c52bf47caa93ddae6736af16bcbad8951130f235648f060f5d2f908003a3a86baed13af94aa0b218020a1168204a6fb59612f3e0730ef70eb08694e952bf698fca80403263b9f5a173d9385e790505b92d366657ab077905|c4bac3714f1ecadf995e7fe3d143541633e9d2eafdd91913b94ea985dea2b12debca3458bbe24dec42074a20bcac337117bca3a9872d88b581b44921ca0abd20a81103847bc99222f725aa2bdb75ad9b3ee93b25504e0d78992077a454643b1300c5297c0e0e7a7f09c71fd3ace8c1ad2a7449e598f52c9d633569e9338ec01dad4040c021d349764e6fc73433ac43523e2b3a3d3d998b570f2590f7e2f38b28f565e970e5d5d454a2cbf8183cd354bc790cfa66d081e1af99e7d9d72ebe5c12|0653ae8264cbd07014f4879fa6ec33413895b878f554ab9193be47ce7c08792f47d1da3011f839ff7386a2e66a03b7b19bf17cdd38976a014a0322350c6fe918f83dc30b87cf11c0c68adacff575177ba074eb0f299d02b99b0ce23643d18a084618c7498543a985492f46128f159d989a1ae7b2d90afcb0d79aba2da34faa21f7ebcc98f85b7f31ed2ce52ca95ef05d213758ac99f63316b18d94ca0369cf16021e28518475b576b57fb981b850227d5725c0785fc973c1bdeebd60ef978023 +mul|8310f51b9d5d7a142188951ae7c506c00d260ae23ce974ec226dbf72e85cc62ceb9e92b0b54e67af29c37a9e9545698b55559f050a7fa2fea1173a75632b4209a04fc68f0299a4e2bdb084fbd0ebc416df5d273579eb0f31352d59db9735c61bffdf73ed7cc502c9c52bf47caa93ddae6736af16bcbad8951130f235648f060f5d2f908003a3a86baed13af94aa0b218020a1168204a6fb59612f3e0730ef70eb08694e952bf698fca80403263b9f5a173d9385e790505b92d366657ab077905|c4bac3714f1ecadf995e7fe3d143541633e9d2eafdd91913b94ea985dea2b12debca3458bbe24dec42074a20bcac337117bca3a9872d88b581b44921ca0abd20a81103847bc99222f725aa2bdb75ad9b3ee93b25504e0d78992077a454643b1300c5297c0e0e7a7f09c71fd3ace8c1ad2a7449e598f52c9d633569e9338ec01dad4040c021d349764e6fc73433ac43523e2b3a3d3d998b570f2590f7e2f38b28f565e970e5d5d454a2cbf8183cd354bc790cfa66d081e1af99e7d9d72ebe5c12|87c3b2a79ff8fd6415678fee414e0fb6a9358282a41306c201d6cfc2e5130620f2c745fe8778fd82713a33da224e5195a0909aec109979656990f329d47d162ba0ce36b7368a94f9b9e508fad34d72f998f7bbb905e4d3ebb73f3b689fff84032992bba65bd6a3312013b1ced4c4e309653f235b90dc26c8ee78f25220588c021823a237c985ef05470bc85a2d39ad32f2d793c58eb4e8428edfc62779f8321894cb3a9e65275112b8ace3acb607ca6e0fad91a78122a2f977eae8408e756426 +square|8310f51b9d5d7a142188951ae7c506c00d260ae23ce974ec226dbf72e85cc62ceb9e92b0b54e67af29c37a9e9545698b55559f050a7fa2fea1173a75632b4209a04fc68f0299a4e2bdb084fbd0ebc416df5d273579eb0f31352d59db9735c61bffdf73ed7cc502c9c52bf47caa93ddae6736af16bcbad8951130f235648f060f5d2f908003a3a86baed13af94aa0b218020a1168204a6fb59612f3e0730ef70eb08694e952bf698fca80403263b9f5a173d9385e790505b92d366657ab077905|-|37a81296c4cd5b88cddb3e810f888f6c24a73c31b3bef831099f7335ad183f1cd36e9e77588cd45f374bcad9f199634e459112a48363fdcc59822d9543f16118ddd146b520bd6cf9ec121e8f6c5786176d08ddf9912a04a7e8b456908bde001599f9d32c6767939502531ad23715a829b63952475e98fa6b6c6986f96b552303aad0d36dee2e98ec414360d2d9bf28fe6eff256b1c8548be8ba70d12d2ace716f05e4223d62288b58c42e57316e889878a13a7ce5473b191c3d35ec6d15be12f +inv|8310f51b9d5d7a142188951ae7c506c00d260ae23ce974ec226dbf72e85cc62ceb9e92b0b54e67af29c37a9e9545698b55559f050a7fa2fea1173a75632b4209a04fc68f0299a4e2bdb084fbd0ebc416df5d273579eb0f31352d59db9735c61bffdf73ed7cc502c9c52bf47caa93ddae6736af16bcbad8951130f235648f060f5d2f908003a3a86baed13af94aa0b218020a1168204a6fb59612f3e0730ef70eb08694e952bf698fca80403263b9f5a173d9385e790505b92d366657ab077905|-|83740fd9b363c5e09c55b187d6ab0eeeec801ca70fab478e2b1e2bfbd2d38c0eeaafb62906a66f5312ae22173302b6ba9448a302681f73fa47cd7bd994ad3027daed0c8c86847e3ea90182abd1751924ca5424d345bfc2fbf0811e84d9193d003316139a204c2a551bcb95bb66f0acf7377e94901b9de3eb609af11c41e40c2747d9497b0d6744fa8ba77ca5494d19cf2999236750a63e1d11fca15cfa1b4b018d3133a6bca0b29e52bc2c77ae3ac8f9b4e08f53a6dbad49c028fdb306aa7e26 +add|e762713bbedcbcdb4a1bf80267829230777d25da80184116efdff8e693fcc01eebf5f37921a65b3998098bb737b09a05584e7b968377fb1db7a1cca90659c9205685f4c527a6e49b7df04e5d6010fa3cb892344868f0d9f56fc4359408513611d9bca9ac14794b37c6d91ea8bbe81d3951067f2a223251e3183498578d0f37151c7fbdf292a257a5bf08f0b0e001647baab79f1fd65347126754e86e33b8c40d83019cfb923dc4055462c6553022f54c77acbdf9169fe29ac0acf1c95e166202|b9f650047cd073f722b2447b8a252867af9d74fa2eab711b8851a24d0f19692808eda338f6da13a381561b9700e7cd4daa0f3f64a470c64ebf0d22949024f80ccb8820230c98475bf1abcbfe1a90fda015be2cb74fb634f6625bb6a1f69cc81b50dbbb7568c5f04608e49a6e77d3d105f4140d557bcf9f60776556fe94192903cd4e07732df8d1472a2d6ac0d52202fdfeabda9f7b65e0bdc0c2feff255dc80e00be7dbdd43d036800d1e76f6be6acb9425279af2475ae6eee05a7741b5ad40d|595c456723211097e002cb15603d3900c9c21853f97d62794d91695330c7c516f3e297b217816fdc1960a64e38976853025ebafa27e8c16c76afee3d977dc12d210e15e9333e2cf76e9c1a5c7ba0f7ddcd5061ffb7a60eecd21fec35ffedfe2c299865227d3e3c7ecebdb91633bcef3e451b8c7f9d01f1439099ee5522296018e9cdc465c09a29ede9355a71b6246678a9637abf51b927d02717e76e59158d1c83bf19b9677bc76d5433aec59b08a206bafe36a93b149109afb2983e7a703610 +sub|e762713bbedcbcdb4a1bf80267829230777d25da80184116efdff8e693fcc01eebf5f37921a65b3998098bb737b09a05584e7b968377fb1db7a1cca90659c9205685f4c527a6e49b7df04e5d6010fa3cb892344868f0d9f56fc4359408513611d9bca9ac14794b37c6d91ea8bbe81d3951067f2a223251e3183498578d0f37151c7fbdf292a257a5bf08f0b0e001647baab79f1fd65347126754e86e33b8c40d83019cfb923dc4055462c6553022f54c77acbdf9169fe29ac0acf1c95e166202|b9f650047cd073f722b2447b8a252867af9d74fa2eab711b8851a24d0f19692808eda338f6da13a381561b9700e7cd4daa0f3f64a470c64ebf0d22949024f80ccb8820230c98475bf1abcbfe1a90fda015be2cb74fb634f6625bb6a1f69cc81b50dbbb7568c5f04608e49a6e77d3d105f4140d557bcf9f60776556fe94192903cd4e07732df8d1472a2d6ac0d52202fdfeabda9f7b65e0bdc0c2feff255dc80e00be7dbdd43d036800d1e76f6be6acb9425279af2475ae6eee05a7741b5ad40d|75699d0f59986920b53325f06dc7eb602538326108b31fb3902e887af731bc26e30850412bcb479616b36f2037c9ccb7ad3e3c32df0635cff793aa157634d113d2f9507b329abd7c190ff5c6d6ea7d33002d8912cf7ff5b73609b1d38402d22589e1ed36acb35af0bdf5833944154c335df171d5a662b182a1ce4159f8f50d12962d33587c36a69922a6f7589c49e315096446011134b70cd0311b5080a9602fca409b16d58be1d9e05b504e56a6c92a92b2c5cba86f84e4fb467c36b60af224 +mul|e762713bbedcbcdb4a1bf80267829230777d25da80184116efdff8e693fcc01eebf5f37921a65b3998098bb737b09a05584e7b968377fb1db7a1cca90659c9205685f4c527a6e49b7df04e5d6010fa3cb892344868f0d9f56fc4359408513611d9bca9ac14794b37c6d91ea8bbe81d3951067f2a223251e3183498578d0f37151c7fbdf292a257a5bf08f0b0e001647baab79f1fd65347126754e86e33b8c40d83019cfb923dc4055462c6553022f54c77acbdf9169fe29ac0acf1c95e166202|b9f650047cd073f722b2447b8a252867af9d74fa2eab711b8851a24d0f19692808eda338f6da13a381561b9700e7cd4daa0f3f64a470c64ebf0d22949024f80ccb8820230c98475bf1abcbfe1a90fda015be2cb74fb634f6625bb6a1f69cc81b50dbbb7568c5f04608e49a6e77d3d105f4140d557bcf9f60776556fe94192903cd4e07732df8d1472a2d6ac0d52202fdfeabda9f7b65e0bdc0c2feff255dc80e00be7dbdd43d036800d1e76f6be6acb9425279af2475ae6eee05a7741b5ad40d|9e267b7f58259bcd1bd23b35c31c6a452d38b2457dfdca6eeef1ad4f15cd9b0d3995bdc884993b1655ec34c057f78aba9221540bc912c83a1d0360308550e7164a3153a0390e9364f39ae3d3e0cc4e9ef3fea2980d3c6ea26bbbe4b43cd50b00d5776d4c780d6233d0f61c27cc25763aeefebc85484165d2c8835ee7932da406480a086c0e99c985ba8500b9a4251604be17d90f6c02facbe06dba8647887527426c0bc8776874249789caf76093aed6e581506cb1b3051824b877e123bf0d1b +square|e762713bbedcbcdb4a1bf80267829230777d25da80184116efdff8e693fcc01eebf5f37921a65b3998098bb737b09a05584e7b968377fb1db7a1cca90659c9205685f4c527a6e49b7df04e5d6010fa3cb892344868f0d9f56fc4359408513611d9bca9ac14794b37c6d91ea8bbe81d3951067f2a223251e3183498578d0f37151c7fbdf292a257a5bf08f0b0e001647baab79f1fd65347126754e86e33b8c40d83019cfb923dc4055462c6553022f54c77acbdf9169fe29ac0acf1c95e166202|-|574b37f73754862282c02f0267bf1bafecbfd19c09f2ef867ef805a7d272b40e126944333bc9df0d3a78e5669e4ad3b9371d2dbfe0f47d03c0ed27f7695bfd2c109b9798f44b943dcd5a38a437ec1ec02cef8a9f0c550e9411882a59e4768c13becf68369968e20d15fb2b66299714d5639de2782b07241fb31ea0447ab912278a811fbb7fb1fcc771c8bec5654c265ae84fe86e9512738f29a4bbc91b809a29e96ab58ae028eaff080ddbed7554c145c65c5ea4b5ebc3225c387ab0fb9dc321 +inv|e762713bbedcbcdb4a1bf80267829230777d25da80184116efdff8e693fcc01eebf5f37921a65b3998098bb737b09a05584e7b968377fb1db7a1cca90659c9205685f4c527a6e49b7df04e5d6010fa3cb892344868f0d9f56fc4359408513611d9bca9ac14794b37c6d91ea8bbe81d3951067f2a223251e3183498578d0f37151c7fbdf292a257a5bf08f0b0e001647baab79f1fd65347126754e86e33b8c40d83019cfb923dc4055462c6553022f54c77acbdf9169fe29ac0acf1c95e166202|-|db02fca2b0253cd97384778a1cf581f68655cd803e6f3b3029881adccdae8c23436132d3b004b32eb3678bc52dc627faeb732668fe3d7184b1c5e441d231800d514a9b1df7c3fb8cf54d0e38db966d2c30b3a8f14e5e36d54edb4cb2f5148911f7926acbaa3c8d977bc11f9405faa6a28523c857295c8dbcccabe1340bec9c23ec871049b4a94efa9d7ba66a80037c76e41fd337629099df80342a3fd73d7f2133b802805ed39694b30e04270c837b68767eb5aac3fae03fbd842889e334cd09 +add|ea873cb87417a8fc7212fa04788acb840f8de2cec67a69482a2bad3a080ee1150709173089a502a0655c8223a453c8f4c555073c7ff405dc59d35d39ef0d301d98011c3950edf8f76e845a8906b37f8335c544e0d5c96f8d3f8c9701fd3ffc2a4b858a25dc176033e7cef68690d23b363aeea998d993195ea79429fb76b8521368a782c3c282219b2e19f0d55ceb14a6cf8e78f653ccb778998c15e8e15c6601804fe864475df9c1eac1f6c8fd9346c70a0b911b7e22757d6ad51f510cc7ac09|e9904d010691006a200cad8135fbda29744a60e6e954c1769d54d7262cce5e08ee47353a0f32bc888784310f899b1fd28fae762baa2489c1c8129190dd65d5173f7cedf432e3dc9d972ec91df352c7391f3d94d9196eeb19628db76ab2e3232bf5fbb7476efd08c93f923f80fd7a7abfc15896769a09741e3348e19bb0936f1d905d5b7ba17e4052149f347bdc1a0868d16e347e550b7c09ed9514e580a7c9208dc2315d7f1e0f1ff2d643319a83c97c35aef887d733d550b4e68770f818072c|d3188ab97aa8a866931ea786ad85a6ae83d742b5b0cf2abfc77f846134dc3f1eae53cf91814b9eec5f1642ca9b84662ff8abfce572d33ee5f845bde85925a10490808c556c44b55979e8b13e689bc525f7a9573839f20aef77791d8b3cd5bb25f983c594338948c09996c49efce2345e9eeebe8dbd573dc4b03cd9b5b4fd5d00f804de3e640162ed42b8245139061d0ea1fdac74a9d7338286222acd62043022c6149de9afefe7a44fcec89106ad8eace26008229f10fa15f51b76e091914f05 +sub|ea873cb87417a8fc7212fa04788acb840f8de2cec67a69482a2bad3a080ee1150709173089a502a0655c8223a453c8f4c555073c7ff405dc59d35d39ef0d301d98011c3950edf8f76e845a8906b37f8335c544e0d5c96f8d3f8c9701fd3ffc2a4b858a25dc176033e7cef68690d23b363aeea998d993195ea79429fb76b8521368a782c3c282219b2e19f0d55ceb14a6cf8e78f653ccb778998c15e8e15c6601804fe864475df9c1eac1f6c8fd9346c70a0b911b7e22757d6ad51f510cc7ac09|e9904d010691006a200cad8135fbda29744a60e6e954c1769d54d7262cce5e08ee47353a0f32bc888784310f899b1fd28fae762baa2489c1c8129190dd65d5173f7cedf432e3dc9d972ec91df352c7391f3d94d9196eeb19628db76ab2e3232bf5fbb7476efd08c93f923f80fd7a7abfc15896769a09741e3348e19bb0936f1d905d5b7ba17e4052149f347bdc1a0868d16e347e550b7c09ed9514e580a7c9208dc2315d7f1e0f1ff2d643319a83c97c35aef887d733d550b4e68770f818072c|01f7eeb66e86a79252064d83428ff05a9b4282e8dc25a8d18cd6d513dc3f820d19c1e1f579734617ded750141bb8a82236a79010d5cf7c1a91c0cca811a85a05a082ab1c34963c96642003d4a4ca39e173e0318872a1d42b079f1178bdaa3c309d864fb684a677a63407296f24c2420ed6ed94a3f5cff5f79dec7940397347261f47a42038900185a7442dc3113b8ed55b78c5f9b4068c27d69632e4d30301113a8a33e0deca0adf85b52400f57afee132b519155d34f0e4df8ec9c186fc090e +mul|ea873cb87417a8fc7212fa04788acb840f8de2cec67a69482a2bad3a080ee1150709173089a502a0655c8223a453c8f4c555073c7ff405dc59d35d39ef0d301d98011c3950edf8f76e845a8906b37f8335c544e0d5c96f8d3f8c9701fd3ffc2a4b858a25dc176033e7cef68690d23b363aeea998d993195ea79429fb76b8521368a782c3c282219b2e19f0d55ceb14a6cf8e78f653ccb778998c15e8e15c6601804fe864475df9c1eac1f6c8fd9346c70a0b911b7e22757d6ad51f510cc7ac09|e9904d010691006a200cad8135fbda29744a60e6e954c1769d54d7262cce5e08ee47353a0f32bc888784310f899b1fd28fae762baa2489c1c8129190dd65d5173f7cedf432e3dc9d972ec91df352c7391f3d94d9196eeb19628db76ab2e3232bf5fbb7476efd08c93f923f80fd7a7abfc15896769a09741e3348e19bb0936f1d905d5b7ba17e4052149f347bdc1a0868d16e347e550b7c09ed9514e580a7c9208dc2315d7f1e0f1ff2d643319a83c97c35aef887d733d550b4e68770f818072c|4ef860ff59b63665de05f992a3d5f47819245f2b034b6b29b6edaa9512cdbb07c654e5ba10a7d4c483bd985acfc311ff1ac0da041d451488301bec7c24b569140b8e7afbec640f94b367a3980a1e1a65a5b7128eb19121d33bac695ad2a8930d7d2c28af21060c3d53638ebff20fbfd7e1d490fcd930c22d00fed0678068291c386b384ca518098f26c6107611d4107ba9457b93d9672c645344054fd5afe32c60cda445bee076f123889e2dcef8c936724890d520a032a22d01ea6658768c09 +square|ea873cb87417a8fc7212fa04788acb840f8de2cec67a69482a2bad3a080ee1150709173089a502a0655c8223a453c8f4c555073c7ff405dc59d35d39ef0d301d98011c3950edf8f76e845a8906b37f8335c544e0d5c96f8d3f8c9701fd3ffc2a4b858a25dc176033e7cef68690d23b363aeea998d993195ea79429fb76b8521368a782c3c282219b2e19f0d55ceb14a6cf8e78f653ccb778998c15e8e15c6601804fe864475df9c1eac1f6c8fd9346c70a0b911b7e22757d6ad51f510cc7ac09|-|68c16e800e52ac7d68edc1c97eef3018b1223f1e47d8662a70e2cdb79235e626b4c482ffac69eac7a7d91510fca12cd79054b9298b02ef9430230923e095010c03625e08608824f8dfb8f7363cc0ef7e4351309d67f7ff0ccc3e8bc595341a14cfc11ca5b9fc3c2532da4e27891d77734fb3434f09be9b6144731578ed9a282ac39ab1a25a533ef9d49dff7ed0be2a4858c96f748e97cb99641b0c28759d831359e9e0d5a743b0d5411531183cd9cb594183b602d27491de92dd97249e427a0f +inv|ea873cb87417a8fc7212fa04788acb840f8de2cec67a69482a2bad3a080ee1150709173089a502a0655c8223a453c8f4c555073c7ff405dc59d35d39ef0d301d98011c3950edf8f76e845a8906b37f8335c544e0d5c96f8d3f8c9701fd3ffc2a4b858a25dc176033e7cef68690d23b363aeea998d993195ea79429fb76b8521368a782c3c282219b2e19f0d55ceb14a6cf8e78f653ccb778998c15e8e15c6601804fe864475df9c1eac1f6c8fd9346c70a0b911b7e22757d6ad51f510cc7ac09|-|e531942a1b0dee78b39c0060d3b3427ec6990ccf6732157d38256d13526e18220d23ee3a38e43772908c663fee09aa603df990b96812dcb5cb026a968c81ef22c2a08f68a120f9d275131f6d978275a1237cd4ebadec41d7f04fe5154c9f830f49e9829224078ea01571c8fc703096bf63a4feb611c7391972a80ac1d1f6a6101d3fe1ec4e017d80e5400328eb6d87efce84d4861e77c25ac08fa5a538b9531c71d9dafa7ace57ac7a7549ed295730515775ccffab4b5e7ffda0c42c34d8c60e +add|9185ef7045606e75f7074da396bce32205c27cdb2909d764482a5605340f730cd6b2a56d698f72dda6bdab6a9989905ae43f75f44db225e384f8b81b0cd6ce236117460f97032d2c0cabd8a94f25cd1eab4e503adf8bee3574c75067b0add40d2ffb91ffc41b7e1ee0f30364ee7b0cc8b44c88a10a335b3a37423c73e2417c1e4d5b25c50994082fc83e2bac5d9bd0ccdfe55f0a6508fa1f590840bb8f02d324ed4cacb7248d3a23f73628c25135e2556888b7eac8f35eef0674dc78eebfd606|e52b6b4952ac2645509805bbd51f64bcf5717f65cd906717cb2db1cf9f3e8021aadab1ec309e2d1c2204319ceb65f086d540bf2eb16a658992156bdac593b1059524486e6320fa5f82a881c1387c3dfa7a46427d1abb3bca6ec1a7978def621611156bd26c54e5484551d94d5c9d9f8fc79aeff2e48d88be50dbb73b4229b422d810950a9d8c035f27adcdc67d6b7cf6cc41eadcee15f6239833db2f4d5c2712bffcddecb83bb823ab55420812e604ccef916122a35daf9c86c6e9645bba4821|76b15aba970c95ba47a0525e6cdc47dffa33fc40f7993e7c135807d5d34df32d808d575a9a2da0f9c8c1dc0685ef80e1b9803423ff1c8b6c170e24f6d1698029f63b8e7dfa23278c8e535a6b88a10a19269592b7f9462a00e388f8fe3d9d3724f91280f91ae4422b987a6b49b9ae2ac01e8ff612397b93405e7dc2cdb11ccc10de6e3df78f94eb516221870a4a9ccb2b4fcfc8659dd89f8bc79be9096a109606ac498aa4ddc8f246a28c6aca631be721581a190d6c510e8c8d3ac6dd497a1f28 +sub|9185ef7045606e75f7074da396bce32205c27cdb2909d764482a5605340f730cd6b2a56d698f72dda6bdab6a9989905ae43f75f44db225e384f8b81b0cd6ce236117460f97032d2c0cabd8a94f25cd1eab4e503adf8bee3574c75067b0add40d2ffb91ffc41b7e1ee0f30364ee7b0cc8b44c88a10a335b3a37423c73e2417c1e4d5b25c50994082fc83e2bac5d9bd0ccdfe55f0a6508fa1f590840bb8f02d324ed4cacb7248d3a23f73628c25135e2556888b7eac8f35eef0674dc78eebfd606|e52b6b4952ac2645509805bbd51f64bcf5717f65cd906717cb2db1cf9f3e8021aadab1ec309e2d1c2204319ceb65f086d540bf2eb16a658992156bdac593b1059524486e6320fa5f82a881c1387c3dfa7a46427d1abb3bca6ec1a7978def621611156bd26c54e5484551d94d5c9d9f8fc79aeff2e48d88be50dbb73b4229b422d810950a9d8c035f27adcdc67d6b7cf6cc41eadcee15f6239833db2f4d5c2712bffcddecb83bb823ab55420812e604ccef916122a35daf9c86c6e9645bba4821|f35601000a40686c343ab950520701fe6ca87ef712bebf05a79cd616071f571b2cd8f38038f144c184b97acead23a0d30effb5c59c47c059f2e24d4146421d1e13f07a794a6f530817cdc850a81311bc8d608f3e7b1603242fa6dab0950cd62765e3a3056f53b911286d9c7e2349eecf4a0a1a30dcea22341007b61813672c2c754a90ba6c0705d0a0915de5df2f54d612a4752d76f203fcc0d4648b42a6ab12754d4ba382dda23bd9ab5722d1b95e21d64ed749dcdbff0aaa4d24f50554f215 +mul|9185ef7045606e75f7074da396bce32205c27cdb2909d764482a5605340f730cd6b2a56d698f72dda6bdab6a9989905ae43f75f44db225e384f8b81b0cd6ce236117460f97032d2c0cabd8a94f25cd1eab4e503adf8bee3574c75067b0add40d2ffb91ffc41b7e1ee0f30364ee7b0cc8b44c88a10a335b3a37423c73e2417c1e4d5b25c50994082fc83e2bac5d9bd0ccdfe55f0a6508fa1f590840bb8f02d324ed4cacb7248d3a23f73628c25135e2556888b7eac8f35eef0674dc78eebfd606|e52b6b4952ac2645509805bbd51f64bcf5717f65cd906717cb2db1cf9f3e8021aadab1ec309e2d1c2204319ceb65f086d540bf2eb16a658992156bdac593b1059524486e6320fa5f82a881c1387c3dfa7a46427d1abb3bca6ec1a7978def621611156bd26c54e5484551d94d5c9d9f8fc79aeff2e48d88be50dbb73b4229b422d810950a9d8c035f27adcdc67d6b7cf6cc41eadcee15f6239833db2f4d5c2712bffcddecb83bb823ab55420812e604ccef916122a35daf9c86c6e9645bba4821|2a402645ff682d0858f1052c9dd5afa5e5fb3acd01e7720d3c7cafdefa22d92b754bae8deed136785b3f45adaabe8f3fee8e2c7a0b51ed06da81084cf2999c1922656308ccbd4fb44eb3e2388f6d17689080a052e91b608a4be69e17e7e05a060bb01717eb4a11fdf91e262d67146c83056fb5bfe956e2a5d42b6faa469c6c141fcd909b1d70c45d50331ff9c40bab69057bee6480d38c6306bbfb6038089d1da5597e7ffcb568448ec2f91eb0c6fd1c3e359b10231be68234eda67677c3fd0e +square|9185ef7045606e75f7074da396bce32205c27cdb2909d764482a5605340f730cd6b2a56d698f72dda6bdab6a9989905ae43f75f44db225e384f8b81b0cd6ce236117460f97032d2c0cabd8a94f25cd1eab4e503adf8bee3574c75067b0add40d2ffb91ffc41b7e1ee0f30364ee7b0cc8b44c88a10a335b3a37423c73e2417c1e4d5b25c50994082fc83e2bac5d9bd0ccdfe55f0a6508fa1f590840bb8f02d324ed4cacb7248d3a23f73628c25135e2556888b7eac8f35eef0674dc78eebfd606|-|c3b413964e0c903decce2e2ced1a4798736796922f6840e6dc740867b8b23503361e1ba99e35d2777283b310a32fc85da35058b3847347a43191aad2fea03915789dfbaafac3627429e483c2c4904508fe1414f2c88120de4538f55ac4b00c0b187f5765879c19ca85d8dda50bd164d37a44a42881d9bbe739895a4800477f03c2e90204e1e86e05f9d2fcc708fb7a6287e64fd3eb510eab75532e993c5e3828fe13ee3989278cb84396e77909fa5844421fa78cbb5bd68277476d35610c370d +inv|9185ef7045606e75f7074da396bce32205c27cdb2909d764482a5605340f730cd6b2a56d698f72dda6bdab6a9989905ae43f75f44db225e384f8b81b0cd6ce236117460f97032d2c0cabd8a94f25cd1eab4e503adf8bee3574c75067b0add40d2ffb91ffc41b7e1ee0f30364ee7b0cc8b44c88a10a335b3a37423c73e2417c1e4d5b25c50994082fc83e2bac5d9bd0ccdfe55f0a6508fa1f590840bb8f02d324ed4cacb7248d3a23f73628c25135e2556888b7eac8f35eef0674dc78eebfd606|-|8116702b71797a1518b7439e8b77a5e4fc5cbd8136a0b0585a67a63a60cc4919a34f49c3875a8c1a7aeeedf53deb4ff403269fafa587af77ded45449d085292ded5e3c6ad5e29d334fd2c7f378715309aee17859f0e1218c523c8040bb18632b7cabad4ca944266ad744fb99a2e4a64327ceba1f1755ab4f5953b0c952cc300f68fbf0889fed94a234678d0ae968cbe277159d29f4e19e25776119f9f95fb80e54785ec089702b389d7b301139adf2a6a460eee5e3dfe6918f0ff6fde4517a26 +add|40be537b1523097f3774835a5ac7514030a12c40ff49b51c0c186d07d488a22de404338475e1b6b2fa451ac387c30e4c9bcdf2ee4110078e7296ea8db600f72c26a7def04e8464480ebb3fbd2e1cd08f4bb6a27c01619c7fc6b7aebb06f96f1a564c4fef9f2169f5d23c00f456e7ade1898108315bd0b3847a08d11f9256890ccc0cb0e29463d758c1a08898c0bc456bab512088973cf044b09f6a6b3ba14a264002f2f2890034fa8e9ad88354843dc7e8f376359c1ab6c61a35a440d9f0811f|b96ae3b2390bb40bbf306ca40be9db63545e658ba8906c9cee6db03ae5b3eb1ecc3aa894e9f07d486d4eaaf2f841b42e8d2591b4d671dfeae9e3efb76c0dfb140c8b41ed2765142e01d73c5cd47513b6da270990755f7dbd2c5301bf9302c82e84dc5e46a804949b75d2636e4b0eb2aa5c16e9308be9e8fa795297320ba13b187c81c5ab56d674e84e9c59b7b767debae6091eb8fd5f3c769e69b70c8a4667215e317b4b3746129a26d7d19a27c747da20f1529300702c156caabcd963084e12|b22bba5538a29c4e69da7d96d445ac0c27a7104af194d100d1e5eb6046ee291c69425e40484614bfdac9524def9a41e3ca9a0222623c96c032daa864b0bf8d11eb34a305605d583a82c70ab1712762aec8852a8bc07ac984c96a7e9927add318da28ae354826fd90480f6462a2f55f8ce697f161e6b99c7ff45a68529df7c4240191f8b5d4ad2b05837270e7e6b9a28e3403bdbede56dc022569f09652994d175736f065aaba255828a738b6eae0030aac8c4847e64492235d3f2f39caaa6b01 +sub|40be537b1523097f3774835a5ac7514030a12c40ff49b51c0c186d07d488a22de404338475e1b6b2fa451ac387c30e4c9bcdf2ee4110078e7296ea8db600f72c26a7def04e8464480ebb3fbd2e1cd08f4bb6a27c01619c7fc6b7aebb06f96f1a564c4fef9f2169f5d23c00f456e7ade1898108315bd0b3847a08d11f9256890ccc0cb0e29463d758c1a08898c0bc456bab512088973cf044b09f6a6b3ba14a264002f2f2890034fa8e9ad88354843dc7e8f376359c1ab6c61a35a440d9f0811f|b96ae3b2390bb40bbf306ca40be9db63545e658ba8906c9cee6db03ae5b3eb1ecc3aa894e9f07d486d4eaaf2f841b42e8d2591b4d671dfeae9e3efb76c0dfb140c8b41ed2765142e01d73c5cd47513b6da270990755f7dbd2c5301bf9302c82e84dc5e46a804949b75d2636e4b0eb2aa5c16e9308be9e8fa795297320ba13b187c81c5ab56d674e84e9c59b7b767debae6091eb8fd5f3c769e69b70c8a4667215e317b4b3746129a26d7d19a27c747da20f1529300702c156caabcd963084e12|875370c8db175573784317b64ede75dcdb42c7b456b948801daabccceed4b60e18ca8aef8bf0386a8df76fd08e815a1d0ea8613a6b9e27a388b2fad549f3fb1761191adc3dab70569aae74c9eb103e71cee61a6e42476f7ac304dfdde5440c1c196d6d810ea9f595ea340eee9c437dce8ac3a081862c1b422a566bcef903b224508bea363e8d627072042fe1085567b0c44702d099dcb3ce1136b35eb15ae304e2d076a752ba216068c306e92cbdf5ecc70224a29baa89b1ae8ae76675e8330d +mul|40be537b1523097f3774835a5ac7514030a12c40ff49b51c0c186d07d488a22de404338475e1b6b2fa451ac387c30e4c9bcdf2ee4110078e7296ea8db600f72c26a7def04e8464480ebb3fbd2e1cd08f4bb6a27c01619c7fc6b7aebb06f96f1a564c4fef9f2169f5d23c00f456e7ade1898108315bd0b3847a08d11f9256890ccc0cb0e29463d758c1a08898c0bc456bab512088973cf044b09f6a6b3ba14a264002f2f2890034fa8e9ad88354843dc7e8f376359c1ab6c61a35a440d9f0811f|b96ae3b2390bb40bbf306ca40be9db63545e658ba8906c9cee6db03ae5b3eb1ecc3aa894e9f07d486d4eaaf2f841b42e8d2591b4d671dfeae9e3efb76c0dfb140c8b41ed2765142e01d73c5cd47513b6da270990755f7dbd2c5301bf9302c82e84dc5e46a804949b75d2636e4b0eb2aa5c16e9308be9e8fa795297320ba13b187c81c5ab56d674e84e9c59b7b767debae6091eb8fd5f3c769e69b70c8a4667215e317b4b3746129a26d7d19a27c747da20f1529300702c156caabcd963084e12|7fbb9b76646bb7a485a3f33eccfbbc9f35593189acffeb13cc186544eba9fb2b2fa91d3b0d45d3605aea1ffd71a6d39e411c16a0e486b5aef978838698e836202c04bf24a9f9fdac46dbb9113a18d5ea15fc9d2184dd431ebc49d21202b401116a2f58c202a71a124872d4c4004ab84bb93d8b7e4f7643c80e0ea0947b7c440a97798a04bd7f078118a050a31a8ae28597c9672e7a52162419e18e61fe7c4b20cc4f95d5cf243840331da2155a5fba58bf0fe36d8c5f74e52a5df3f01ba47f22 +square|40be537b1523097f3774835a5ac7514030a12c40ff49b51c0c186d07d488a22de404338475e1b6b2fa451ac387c30e4c9bcdf2ee4110078e7296ea8db600f72c26a7def04e8464480ebb3fbd2e1cd08f4bb6a27c01619c7fc6b7aebb06f96f1a564c4fef9f2169f5d23c00f456e7ade1898108315bd0b3847a08d11f9256890ccc0cb0e29463d758c1a08898c0bc456bab512088973cf044b09f6a6b3ba14a264002f2f2890034fa8e9ad88354843dc7e8f376359c1ab6c61a35a440d9f0811f|-|1a688e225c0869628494bd7eb2ad01b8112a6e20e73d9c5c8347fe8b46b3fc07cb002009e157e4d62ec3858314d4d837b4d19223935be44410fcc58bf0506700fab35ba3ca9a189074a67c20be596122d6177186cc27a12c51598e3389feef272a6d8e9e31a37a8bc99e95fc0785882a804c0694371a94f81b836d40867bdc1443bda303c47d7dcbbbb8062570a1d0e77dceea0ba266cc05f0339b32c420360f84fb251b03c9f0e1a0c2f16064a3bd90adfaebd2bbe6eab5d9f181881865d420 +inv|40be537b1523097f3774835a5ac7514030a12c40ff49b51c0c186d07d488a22de404338475e1b6b2fa451ac387c30e4c9bcdf2ee4110078e7296ea8db600f72c26a7def04e8464480ebb3fbd2e1cd08f4bb6a27c01619c7fc6b7aebb06f96f1a564c4fef9f2169f5d23c00f456e7ade1898108315bd0b3847a08d11f9256890ccc0cb0e29463d758c1a08898c0bc456bab512088973cf044b09f6a6b3ba14a264002f2f2890034fa8e9ad88354843dc7e8f376359c1ab6c61a35a440d9f0811f|-|87cdd45b87d90d2acf1ffefaff838ce824acc6310dafb3b48598733e1af8db2736fbc4b1efeb9487cad04a4d8dd53683af2fd17fe9c560edf1fa57e57481bf2ca1b2743bbaa53dbf99e707db7143fc6beb8877af8556a9de329988a2bd0efa065f9b5d7d5348b8a0624e132f09e1cf38faaddfc4cb04732f108bd60edb751d1b968cc0e324009aa28720a4e12f8ece62b7b4c25c3be185dfe13544175d90e00feb7141412fe786280c3acd0bad2c59c72e7ca7ddb13d84081282219078bd471f diff --git a/testdata/zolt-arith-diff/field/fp_ops.txt b/testdata/zolt-arith-diff/field/fp_ops.txt new file mode 100644 index 00000000..0a0be514 --- /dev/null +++ b/testdata/zolt-arith-diff/field/fp_ops.txt @@ -0,0 +1,24 @@ +# op|a_be_hex|b_be_hex|expected_be_hex +add|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000001 +sub|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46 +mul|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000000 +add|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000002|0000000000000000000000000000000000000000000000000000000000000003 +sub|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000002|30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46 +mul|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000002|0000000000000000000000000000000000000000000000000000000000000002 +inv|0000000000000000000000000000000000000000000000000000000000000001|-|0000000000000000000000000000000000000000000000000000000000000001 +add|000000000000000000000000000000000000000000000000000000000000000b|0000000000000000000000000000000000000000000000000000000000000013|000000000000000000000000000000000000000000000000000000000000001e +sub|000000000000000000000000000000000000000000000000000000000000000b|0000000000000000000000000000000000000000000000000000000000000013|30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3f +mul|000000000000000000000000000000000000000000000000000000000000000b|0000000000000000000000000000000000000000000000000000000000000013|00000000000000000000000000000000000000000000000000000000000000d1 +inv|000000000000000000000000000000000000000000000000000000000000000b|-|046635ad5a4a5460e235d7cac5f47c659968db247ddbcc9879d469d3880b5cd8 +add|12fe805952a503cfd816f9923470dcf4f2653df23f049aca320ce02568fa622c|288e838b7b41234f3924b1fb6af18096d21d97c51461beb98e2376232c82cb68|0b28b571ecb486f558eb65d71de1052e2d016b25eaf48ef6840fca31bd00304d +sub|12fe805952a503cfd816f9923470dcf4f2653df23f049aca320ce02568fa622c|288e838b7b41234f3924b1fb6af18096d21d97c51461beb98e2376232c82cb68|1ad44b40b89580aa57428d4d4b00b4bbb7c910be9314a69de009f61914f4940b +mul|12fe805952a503cfd816f9923470dcf4f2653df23f049aca320ce02568fa622c|288e838b7b41234f3924b1fb6af18096d21d97c51461beb98e2376232c82cb68|186bf1ee65b24e10efd29c61d606f998d671bcb4cec372bac23a0e6083c681eb +inv|12fe805952a503cfd816f9923470dcf4f2653df23f049aca320ce02568fa622c|-|1ff3460cf855dd9a5efdf646f539dd7ecf6b7651506ef6b0875a4eb35e8113f0 +add|147cdfbf8d9f93b0269c74d5ccc7b56cfdc2ebf4a8e1fa8065c474d9f8145afa|1419ff54e0f069c55d3c597d8b35b4244e6414dc4a1567076f77a3779ff34e0c|2896df146e8ffd7583d8ce5357fd69914c2700d0f2f76187d53c18519807a906 +sub|147cdfbf8d9f93b0269c74d5ccc7b56cfdc2ebf4a8e1fa8065c474d9f8145afa|1419ff54e0f069c55d3c597d8b35b4244e6414dc4a1567076f77a3779ff34e0c|0062e06aacaf29eac9601b5841920148af5ed7185ecc9378f64cd16258210cee +mul|147cdfbf8d9f93b0269c74d5ccc7b56cfdc2ebf4a8e1fa8065c474d9f8145afa|1419ff54e0f069c55d3c597d8b35b4244e6414dc4a1567076f77a3779ff34e0c|2da0292c59255736e32c7430c2d7a29e1fbf542e0c757d14c504390cac380f73 +inv|147cdfbf8d9f93b0269c74d5ccc7b56cfdc2ebf4a8e1fa8065c474d9f8145afa|-|169efe495cdcfd7bf35c93b0dbfd510a318921667d99b992bf1fd262e164bfdd +add|14a603890accf93aa6f49621588982d727e22d4f297b1a375124c62114a49fd3|10d8d18e8328f5aceb438810201b4946ae04dccb20f834d6e8947ccd071a8a7e|257ed5178df5eee792381e3178a4cc1dd5e70a1a4a734f0e39b942ee1bbf2a51 +sub|14a603890accf93aa6f49621588982d727e22d4f297b1a375124c62114a49fd3|10d8d18e8328f5aceb438810201b4946ae04dccb20f834d6e8947ccd071a8a7e|03cd31fa87a4038dbbb10e11386e399079dd50840882e560689049540d8a1555 +mul|14a603890accf93aa6f49621588982d727e22d4f297b1a375124c62114a49fd3|10d8d18e8328f5aceb438810201b4946ae04dccb20f834d6e8947ccd071a8a7e|24b8e8ec2ee6d520dd74aa692fb9ba699151093c050e6e8c1996d0a0d6a89229 +inv|14a603890accf93aa6f49621588982d727e22d4f297b1a375124c62114a49fd3|-|163eb133508da79f3fe71d525d8b9c0007c91cef4f8c8fbd744ac6cff9c852fc diff --git a/testdata/zolt-arith-diff/field/fr_ops.txt b/testdata/zolt-arith-diff/field/fr_ops.txt new file mode 100644 index 00000000..d4308734 --- /dev/null +++ b/testdata/zolt-arith-diff/field/fr_ops.txt @@ -0,0 +1,24 @@ +# op|a_be_hex|b_be_hex|expected_be_hex +add|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000001 +sub|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000 +mul|0000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000000 +add|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000002|0000000000000000000000000000000000000000000000000000000000000003 +sub|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000002|30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000 +mul|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000002|0000000000000000000000000000000000000000000000000000000000000002 +inv|0000000000000000000000000000000000000000000000000000000000000001|-|0000000000000000000000000000000000000000000000000000000000000001 +add|0000000000000000000000000000000000000000000000000000000000000007|0000000000000000000000000000000000000000000000000000000000000003|000000000000000000000000000000000000000000000000000000000000000a +sub|0000000000000000000000000000000000000000000000000000000000000007|0000000000000000000000000000000000000000000000000000000000000003|0000000000000000000000000000000000000000000000000000000000000004 +mul|0000000000000000000000000000000000000000000000000000000000000007|0000000000000000000000000000000000000000000000000000000000000003|0000000000000000000000000000000000000000000000000000000000000015 +inv|0000000000000000000000000000000000000000000000000000000000000007|-|06e9c21069503b73ac9dc0d0edede80d4ee2d80a5a8834a709b290cbfdb6db6e +add|02ce7e96d4aee2b1ff23953cddb8a6a2062c9c05c34d4ab4e650849099739d48|28d86dd716982b8c35716d854642ed22cb50703723ad1e47c7b4585a1149410b|2ba6ec6deb470e3e349502c223fb93c4d17d0c3ce6fa68fcae04dceaaabcde53 +sub|02ce7e96d4aee2b1ff23953cddb8a6a2062c9c05c34d4ab4e650849099739d48|28d86dd716982b8c35716d854642ed22cb50703723ad1e47c7b4585a1149410b|0a5a5f329f48574f82026d6e18f711dc6310141719599cfe627e21ca782a5c3e +mul|02ce7e96d4aee2b1ff23953cddb8a6a2062c9c05c34d4ab4e650849099739d48|28d86dd716982b8c35716d854642ed22cb50703723ad1e47c7b4585a1149410b|1f5f249524a2ff26ca5cadf475dc436653e57cddce6f126ec8262bc80bbc4565 +inv|02ce7e96d4aee2b1ff23953cddb8a6a2062c9c05c34d4ab4e650849099739d48|-|305e01ff947f311bdc1cb3277091f69f673280d4996259b0cab06c124cc30f01 +add|05126b2ed4c79ed28a2f8c1ca24a6f999eae3805da6b9913456b6288191f62a0|0d0aae7b514e3bb1d516e73991da3555a08fdd695fa4c68073f3ff4020de73c1|121d19aa2615da845f4673563424a4ef3f3e156f3a105f93b95f61c839fdd661 +sub|05126b2ed4c79ed28a2f8c1ca24a6f999eae3805da6b9913456b6288191f62a0|0d0aae7b514e3bb1d516e73991da3555a08fdd695fa4c68073f3ff4020de73c1|286c0b2664ab034a6d68ea9991f192a1265242e4f4804324155958dbe840eee0 +mul|05126b2ed4c79ed28a2f8c1ca24a6f999eae3805da6b9913456b6288191f62a0|0d0aae7b514e3bb1d516e73991da3555a08fdd695fa4c68073f3ff4020de73c1|2de13436cb5a1002f1d5487469a2d7f548c774844c5850bf5e69408219c23160 +inv|05126b2ed4c79ed28a2f8c1ca24a6f999eae3805da6b9913456b6288191f62a0|-|2f73ff7b49bf9e466f872d44b6dfba3272ca75d450a469bb96062e1e1ba7beb1 +add|13ec3fad92e59e858eedfe3fbc787e601042ff826565a4faecd1ff9a0afd2eb6|1475ffe9330622777aec3e2b8a11988fdb14432c50b0fe30cdd1c71daca0b53e|28623f96c5ebc0fd09da3c6b468a16efeb5742aeb616a32bbaa3c6b7b79de3f4 +sub|13ec3fad92e59e858eedfe3fbc787e601042ff826565a4faecd1ff9a0afd2eb6|1475ffe9330622777aec3e2b8a11988fdb14432c50b0fe30cdd1c71daca0b53e|2fda8e3741111c37cc5205cab3e83e2d5d62a49e8e6e175b62e22e104e5c7979 +mul|13ec3fad92e59e858eedfe3fbc787e601042ff826565a4faecd1ff9a0afd2eb6|1475ffe9330622777aec3e2b8a11988fdb14432c50b0fe30cdd1c71daca0b53e|182127f9554cbad72100982b0e06b6bdc0d7645d96555b70a4ec61ec43b76eb8 +inv|13ec3fad92e59e858eedfe3fbc787e601042ff826565a4faecd1ff9a0afd2eb6|-|1bd0e6820b30be9f81d20e40d0d77237444acaf19c6478ca55ada62ba90feaa7 diff --git a/testdata/zolt-arith-diff/g2/g2_ops.txt b/testdata/zolt-arith-diff/g2/g2_ops.txt new file mode 100644 index 00000000..37d08323 --- /dev/null +++ b/testdata/zolt-arith-diff/g2/g2_ops.txt @@ -0,0 +1,17 @@ +# op|arg1_be_hex|arg2_be_hex|expected_infinity|expected_x_c0_le|expected_x_c1_le|expected_y_c0_le|expected_y_c1_le +scalar_mul|0000000000000000000000000000000000000000000000000000000000000000|-|1|||| +scalar_mul|0000000000000000000000000000000000000000000000000000000000000001|-|0|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018|c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19|aa7dfa6601cce64c7bd3430c69e7d1e38f40cb8d8071ab4aeb6d8cdba55ec812|5b9722d1dcdaac55f38eb37033314bbc95330c69ad999eec75f05f58d0890609 +scalar_mul|0000000000000000000000000000000000000000000000000000000000000002|-|0|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19 +scalar_mul|0000000000000000000000000000000000000000000000000000000000000003|-|0|f5c7fb9406fade12005e9ab08c477e8d5e7192e12628e551900eb14d784e0606|856eb6ff324f82c9a7a078686b1504bcebe4cf5dcd9151734297bb572f771410|97556c66576d036597e72145d720b969a0682c8d9c0f4b07e0b9b581561d8e05|b2117714caea2a454154b549ac5394dd23338df3c2fc2f92b74b35f335231e02 +scalar_mul|000000000000000000000000000000000000000000000000000000000000002a|-|0|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11|919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097412|72c1c48de52409d357618aac72fcbe14cf59f05162b5449630b6772e04416407|db41a40b7cd0c6a84e832bd28bc179c9c5ac040dd0ed7d4a6bf8e516982d2225 +scalar_mul|000000000000000000000000000000000000000000000000deadbeef12345678|-|0|31ace44ca364f4992a358bf050ef2935900504384d82397d07aaf9960a3ed21a|b7f71a7b71fe0a729c3f9863629d8bf99f6775751ff9df0afc528c67329c5914|1c9bbc4304eb9e58e811f06cc35b685aa12401d2d3d9bbe53f15f9a46767221a|9b8382f13ee704be4e8300c680a43117c9c983de0d3b4f53a067aeb8d1db6004 +scalar_mul|12642c234a2832bc1e466fd41b6e68e9a74b1cf50c3c2532f57d1b41135bff8e|-|0|d4365d8ba1ed23445a0e82a3c4d6f806fbd37f156b28df80fb0075b6d0b90a2c|6ebfebd591f8901b93faf518a43e4df2273e39974136a3e4b0e50092587c412b|abc06ba1ecc9c4b9ea171913538be2e346bc460c4a4836178905522d53ab6f1c|a466885c8df4607bc8bb6f5993c25d2312d1c3915777b3ed0745097ac1a39817 +scalar_mul|24b822660f774d469fb39393ed701b8a7b10a241878da51336a6df2803ea0fc6|-|0|1f4ab84c09edc218f8fb10ac60ead025e4083c916be4fbc9be0deb6377305205|36ef4e6ccb32eb11c14b62122e09ce44f58a4306f51ee6fac42ea52049883c0a|205d2a51872fbc18e95e65ed859b3d5da68bc74a3e72ec36421ee554041f9b05|f1955a06faf56b2d8c5a65a31d4061e21d1404ce3d88b087039d87075a42291a +scalar_mul|1a4e9bef842ed025cdf0d873bd15c251ca61a792db0f451e4c01ab1b30dd460a|-|0|efa676ad1a73c58ae22b5a321c7866e17906a33b2ff0fe236173267611021414|9b4f28ceea35fe9e87c6bf05ba8c2992b04c7a2481120534b756898ec4526f2b|ea419f2b0ccf19dd4b834fe3eb24bc8b59eecd1c1ea9129a61518e698e881700|4ff22fb055eb1e8733962d82a7e5379b5be9d313e06e4b3a185a46bff8682a06 +add|0000000000000000000000000000000000000000000000000000000000000001|0000000000000000000000000000000000000000000000000000000000000001|0|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19 +add|0000000000000000000000000000000000000000000000000000000000000003|0000000000000000000000000000000000000000000000000000000000000005|0|299a4d987822792197e48b38a1ecb766009f986d3b213c3802c9f18dfdc7af18|99a6bf88b50d4b487977d44919b4bdaa3911a220b7a2b504167985df20955803|ec9b6037d1cf445780de795dca0927b140820096aa4d45f8cd6b17909e3e6217|2acd931717103fcec72808bf7617c5a86d5ed766e9f3f8575fb7a3f48259c22c +add|23a847559bedadd4bd50533c63e3ab586c2ff57ffcabbd18c1e53e0d78e5de56|056a5084dd129d8f1f3d92a379b6c726de84b1374be60e7d76e665464580ec4e|0|44814162a46c4488583562b9e591b252dbf7bd8058a5f88c43865b959149cf12|5dcb36b284fed218e77bfba6804577bacc95b2dd8081daebd27337e61422062f|72ad2868330a76842e2c2f651a8692e9ec1c57911f5099d802db0e71c27caf0b|09b59a0f7de6677c50e954d4d208e4237a6df621b8094b65c357777688613629 +double|0000000000000000000000000000000000000000000000000000000000000001|-|0|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19 +double|000000000000000000000000000000000000000000000000000000000000002a|-|0|fd59c59d3e0add5617a8f386fc7384ccff70676f059aa778e00082dc96873610|4c053af86854808c99eb24fce630ce706479d0107227261ffa69efe154cbdc2b|6558e0a6be6fa28ca7407796066751646bc864e4efcbf1104bacbf968dbe420e|94dc529fa6f9cb830f36a2efcf95c8122bd241051f6a582b1c4cbeddc4b71215 +neg|0000000000000000000000000000000000000000000000000000000000000001|-|0|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018|c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19|9d7f827115c039ef11f72d5c2883afb3cd17b6f335d4a46d3e32a505cdef9b1d|ec655a073ab173e6993bbef75d3936dbc724751809acb1cbb3afd188a2c45d27 +neg|000000000000000000000000000000000000000000000000000000000000002a|-|0|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11|919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097412|d53bb84a316717693569e7bb1e6ec2828efe902f54900b22f9e9b9b26e0d0029|6cbbd8cc9abb59933e47469605a907ce97ab7c74e657d26dbea74bcada20420b diff --git a/testdata/zolt-arith-diff/glv/glv_g1_scalar_mul.txt b/testdata/zolt-arith-diff/glv/glv_g1_scalar_mul.txt new file mode 100644 index 00000000..7968bfb5 --- /dev/null +++ b/testdata/zolt-arith-diff/glv/glv_g1_scalar_mul.txt @@ -0,0 +1,11 @@ +# name|scalar_be_hex|expected_infinity|expected_x_le|expected_y_le +zero|0000000000000000000000000000000000000000000000000000000000000000|1|| +one|0000000000000000000000000000000000000000000000000000000000000001|0|0100000000000000000000000000000000000000000000000000000000000000|0200000000000000000000000000000000000000000000000000000000000000 +two|0000000000000000000000000000000000000000000000000000000000000002|0|d3cf876dc108c2d3a81c8716a91678d9851518685b04859b021a132ee7440603|c4a2185a7abf3effc78f53e349a4a6680a9caeb2965f84e7927c0a0e8c73ed15 +small_42|000000000000000000000000000000000000000000000000000000000000002a|0|343f9b2f88177d5317c5e46d58775535fbf727aefd9a8f7cd71f97b65df38809|03b24c3b2cdb2cd50599f495aea8b46028d56d6e0a390770c6c8af3fa6ffba23 +medium|000000000000000000000000000000000000000000000000deadbeef12345678|0|682f5ea7383ae04e80483382e36f9c64140be65660412b684c31e5c761c9c916|2c1955bdd1b88609d7794dde95de31dde662ecc0eb949a4de99f5c047b305b01 +random_0|1d147721e8f60b45009109e680d0d5d5c723fb9c79adc76dc6ae5c216db95521|0|c8b206be7c0d8524a73a0202fb5fdb05da66d65966ef54060f9f2a05443bb104|639190b6f455628014d02ec6a3fa16501634e519b83a1d4c762876a16ffc0f2f +random_1|279a1640c54a35fed39e6e9822a1671aae14ec8fab53a6cfdaa6e976e9267e40|0|5cc78fd4268ae35ff475446c563d4088434e21f74e25d7d398c6cd179714ce17|30acc9e8af7274799c744b96fccb1bd4e5cf31d13ecf46915e36ea459fabd82e +random_2|0c67fab0b8d427e7ac2d47859d8dfeea956f41f2f622ea509e39f1898d909c2d|0|0adf00494728e01994f492d0ad942d64c4f6f831b6f489a386c8a24fedae5c1a|557044be98c91acd73d77f6c90d57b4f9a3c605b86e1e7978f6af01557dadc27 +random_3|1efba8eabb15580c6e0e440965a9870477f607ab8918f217fe75b7980e70129e|0|fb6403bbbbee9780b8665726cd5f1b3f187774e586c57ff7f0786f24ba49492e|3f00d98222cd47abc4d6e0acab54b7ce15d30f7b97326a78596c9d5894d85202 +random_4|0eb059d41da72313c6dbd2bf01b3a68ffe93ea4ab1fb93352e3c055c8643b33f|0|cc88e464475a1041b8e4b08d6ac68331bbf8417d567d06bc7816560a11b3650e|902155d8c7e8f07c52ba0900957deefa75db36c68ee36a0541d5e63292743e20 diff --git a/testdata/zolt-arith-diff/glv/glv_g2_scalar_mul.txt b/testdata/zolt-arith-diff/glv/glv_g2_scalar_mul.txt new file mode 100644 index 00000000..5148fe4b --- /dev/null +++ b/testdata/zolt-arith-diff/glv/glv_g2_scalar_mul.txt @@ -0,0 +1,11 @@ +# name|scalar_be_hex|expected_infinity|expected_x_c0_le|expected_x_c1_le|expected_y_c0_le|expected_y_c1_le +zero|0000000000000000000000000000000000000000000000000000000000000000|1|||| +one|0000000000000000000000000000000000000000000000000000000000000001|0|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018|c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19|aa7dfa6601cce64c7bd3430c69e7d1e38f40cb8d8071ab4aeb6d8cdba55ec812|5b9722d1dcdaac55f38eb37033314bbc95330c69ad999eec75f05f58d0890609 +two|0000000000000000000000000000000000000000000000000000000000000002|0|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19 +small_42|000000000000000000000000000000000000000000000000000000000000002a|0|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11|919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097412|72c1c48de52409d357618aac72fcbe14cf59f05162b5449630b6772e04416407|db41a40b7cd0c6a84e832bd28bc179c9c5ac040dd0ed7d4a6bf8e516982d2225 +medium|000000000000000000000000000000000000000000000000deadbeef12345678|0|31ace44ca364f4992a358bf050ef2935900504384d82397d07aaf9960a3ed21a|b7f71a7b71fe0a729c3f9863629d8bf99f6775751ff9df0afc528c67329c5914|1c9bbc4304eb9e58e811f06cc35b685aa12401d2d3d9bbe53f15f9a46767221a|9b8382f13ee704be4e8300c680a43117c9c983de0d3b4f53a067aeb8d1db6004 +random_0|1d147721e8f60b45009109e680d0d5d5c723fb9c79adc76dc6ae5c216db95521|0|5e78318d6717b7f4b66d2c97f7c807c88f067e8c67fa642f263009300e1aae26|f6fa67c4b85d3823bcdce86c8a2d194005b77ac86650a0d0f9143976b29b8810|6e3edc2f9f72487d93da71ad933adf14bec4b14a6962338252e3e493367e5816|936799acf6f44c8ac55362fded926350631b35e7fba3f6d658c164fe91f0ae0d +random_1|279a1640c54a35fed39e6e9822a1671aae14ec8fab53a6cfdaa6e976e9267e40|0|4f6634066ead5df8b4a46df75cf7f07ebcfafa527e9b839c5b9b71b89e467e1c|9fa14c082d3f66eb5c0e88c64384bb1b539e546cb1c7459ae59dbde311e75927|b2cbabb4e6d821697a3f4e939a69257f56555b307e92df58bf4671e314d86c0f|b8a1be24743d8f4bfbb62fb819803d34bde7124c960c84f5bb82cad2d277c407 +random_2|0c67fab0b8d427e7ac2d47859d8dfeea956f41f2f622ea509e39f1898d909c2d|0|513385bdb5bf7a18edb5c75fdcef9593411e6be2cd9cf23272654c67d5619d05|44b6f63a004e19ffa6c1bcc6fe5c97f6dfdc3c54363c30b7ba582dc5af95be13|f390c407e0df6b126f4ad88b5f8d4b43a1a47680c10ea835225abd6d36b2b81d|8e60be5dd67d44f855baf025cec3be2400f6df4ac8731e697c80e6e0d7c48c07 +random_3|1efba8eabb15580c6e0e440965a9870477f607ab8918f217fe75b7980e70129e|0|ed093a5ec5886814ebba6b0aaba6ed0707ae131a915805c3f4a0a4356d576f14|aef9cc82ea969633edff5d9e59402433fdfe2357a5890eb4029c5572be548a1f|a6fd432d30706442cb051a6ca54cb1e87c934f157efcc1cc5aff9504a7428215|683e1607d445f2848d7e2d76cffe7f068951a15f7da8c521d090b40915b0f11a +random_4|0eb059d41da72313c6dbd2bf01b3a68ffe93ea4ab1fb93352e3c055c8643b33f|0|5ed8b04e9162b649680ee47e4fc4e6e6758280aee506cac28d1cfaf0d756b118|a8ea4edac5195c030bc6a375c875c9ee56fc138fd313afa1ff0bcace8167061e|152b2d972bc295e1bf4cf9ca3313cb4a3c9347122d18d496aaf4ff94b2e4f209|a30377151a6c04eb3210969c13cb59a73de226a6f1168799c31d609db81f5424 diff --git a/testdata/zolt-arith-diff/gpu/field_crossover.txt b/testdata/zolt-arith-diff/gpu/field_crossover.txt new file mode 100644 index 00000000..f1041da6 --- /dev/null +++ b/testdata/zolt-arith-diff/gpu/field_crossover.txt @@ -0,0 +1,13 @@ +# op|size|a_be_hex_csv|b_be_hex_csv|expected_be_hex_csv +mul|4|1f872f8b8f3fa570f5408447d19527414fc3d7aaa5e2b7b36229e9c0f707f2d2,0ef0fe9afc5066081b09b40125939c7e38c144d7f2e83cf7ac0411a6052b6c17,0f197754ed7a345cf13a412d410dd1d0196cd2e7c1fe277d1e5b657904813a85,1d222a6de3a94026482eee5a790d4daacff3fb045c04967bca99c85629cc71f5|0a442818b5d475975966c7e02401b3120ed25487620848793085825805b64d0e,1539b2c410d7288cd7b4e35f4d386412fb07987c3ca3279b5db221026c3bafde,15d37537bf86f1ad57fba8f73146fca35c9b1ca134ef8eba1fafb6b8187d6cc6,2ad766062795ae7875627306b6a52b379ecee16e7ffadadf95c4a0743143263e|1388be6f4ccbb77cc0bd9888bd624004455fdc9b66193be349e7fd9972fc99f4,0380812b2233c98bfc12a1570461478609a1ad9ea5d48628cc07de130c438d26,056144c1e959c8eac1cb0ca559b432d09b142cd619da5f492c3b45a38c2cac0c,0bc5ae5262b32ba21d8f98cfe968d053886fd9dc1f49e8e7f341041465cb2c52 +add|4|1f872f8b8f3fa570f5408447d19527414fc3d7aaa5e2b7b36229e9c0f707f2d2,0ef0fe9afc5066081b09b40125939c7e38c144d7f2e83cf7ac0411a6052b6c17,0f197754ed7a345cf13a412d410dd1d0196cd2e7c1fe277d1e5b657904813a85,1d222a6de3a94026482eee5a790d4daacff3fb045c04967bca99c85629cc71f5|0a442818b5d475975966c7e02401b3120ed25487620848793085825805b64d0e,1539b2c410d7288cd7b4e35f4d386412fb07987c3ca3279b5db221026c3bafde,15d37537bf86f1ad57fba8f73146fca35c9b1ca134ef8eba1fafb6b8187d6cc6,2ad766062795ae7875627306b6a52b379ecee16e7ffadadf95c4a0743143263e|29cb57a445141b084ea74c27f596da535e962c3207eb002c92af6c18fcbe3fe0,242ab15f0d278e94f2be976072cc009133c8dd542f8b649309b632a871671bf5,24ecec8cad01260a4935ea247254ce737607ef88f6edb6373e0b1c311cfea74b,179542012a0d4e7505411baaae312085468ef42a624600ca1c7c73366b0f9832 +sub|4|1f872f8b8f3fa570f5408447d19527414fc3d7aaa5e2b7b36229e9c0f707f2d2,0ef0fe9afc5066081b09b40125939c7e38c144d7f2e83cf7ac0411a6052b6c17,0f197754ed7a345cf13a412d410dd1d0196cd2e7c1fe277d1e5b657904813a85,1d222a6de3a94026482eee5a790d4daacff3fb045c04967bca99c85629cc71f5|0a442818b5d475975966c7e02401b3120ed25487620848793085825805b64d0e,1539b2c410d7288cd7b4e35f4d386412fb07987c3ca3279b5db221026c3bafde,15d37537bf86f1ad57fba8f73146fca35c9b1ca134ef8eba1fafb6b8187d6cc6,2ad766062795ae7875627306b6a52b379ecee16e7ffadadf95c4a0743143263e|15430772d96b2fd99bd9bc67ad93742f40f1832343da6f3a31a46768f151a5c4,2a1b9a49ccaadda4fba5165859dc90c865ed94a42ffe85ed9233e63788efbc3a,29aa50900f24e2d9518eddec91482d89e5059e8f06c80954428da454dc03cdc0,22af12da9d4531d78b1cc10a43e97ad0595901de55c32c2d78b71d75e8894bb8 +neg|4|1f872f8b8f3fa570f5408447d19527414fc3d7aaa5e2b7b36229e9c0f707f2d2,0ef0fe9afc5066081b09b40125939c7e38c144d7f2e83cf7ac0411a6052b6c17,0f197754ed7a345cf13a412d410dd1d0196cd2e7c1fe277d1e5b657904813a85,1d222a6de3a94026482eee5a790d4daacff3fb045c04967bca99c85629cc71f5|-|10dd1ee751f1fab8c30fc16eafec311bd870109dd3d6b8dde1b80bd2f8f80d2f,21734fd7e4e13a219d4691b55bedbbdeef72a37086d1339997dde3edead493ea,214ad71df3b76bccc71604894073868d0ec71560b7bb49142586901aeb7ec57c,13422404fd8860037021575c08740ab2583fed441db4da1579482d3dc6338e0c +mul|16|2df2b814bdb50682e00663ccd723624504fc160b72c64569bdfe25ac265f8b87,01d39840667efda722764c0e3c232c39ce99437169a9a0612880d367392f80aa,071e92bcb904417ea8bf884b8e40fa987e50a654e0f4e57a0bc7e336246f6b9e,21d3d07c39ca7dde40a70dc4d6f1cf140009c5b7776582d92b26bf1978aeafab,12eaa958a4064645b4f3ea092c24e7eec9cbfaa14d0f985d7ea14b8e8f4bc62f,1b767ea32dcda5e37f0ddecbf78a4dc4e50de4af192638cc0c55de4300f336a4,181a6dea8e589071456362d13d83d2cbec6e5e8327c814da500cde283570dac5,031eb006c71edbbf537de77ee054ec682ac8097436ae5cfb6d9f3a375a6daf16,132ebcd92ec1461a1da8242092b19a1cc5a41ecdfa0cb50353fd967708811969,03cde8c0a415986ababb06f87790923a233b9f7771a5693f5859d73134ad969c,0b782fc005080a85d027a31bf635edcba14217ce8a91edf7480a8ae94d0e5d6b,0624401e0e35d17faa1ba9a4406800c64fe5d6e5433becfe00339cc6fd6d0f66,0959c73a5b5aa530c7bb3a323b8df511814173eb3cf6a2da4e403e78249ddbe4,0cdb99d510ef76f5aa3645445ed87bd7b695cd532b9da9762592ca581ea07cb0,1fcd4a35d9ed073ad7b47334797d18bc61a4aa34abcf3a1302862e2b1b290e76,2168ac0135571c901e167f2b723cee92f5a6dcc5f4d7648597b7dc8811e9ea2c|205a8f23244e1f6698294149ecc45171c424eb99a082d65a42dc7a16e8fd0352,1387e9312ef4bb0f77e4769faf941ebbc248f5242b02b6c4f1cf6d3bd1cbde5b,2165fe88d3937f146622eb619e52efb555e49744d5a6d9d496c94945c786e81a,1c324d561762dd15df0c9edd92b5cdc578e1a037f946ebac748bb81674590bd9,06e30b907d5288c2948bee824b4568ea315b6a7e156fdcd569c2d76ceb399be5,2cacc853b22775f319ba32978b2f6ec04a1caabdf913cf63b50f23d966d232ba,1aad2cb19feda5ab09350f2b75daf5cd7f5b9dbad70b0ee86a601105d266e8b0,2f5e92af6ac1263b25aa373abe4204434cda5b8d2d2418d5e9fc762cbd3aab0e,1b3e274f78b47ac265f326d7491a0dc055d04d69a303ac41b9ffbc1aa245a6a0,18b0b3fe910134f3468c6c08e5a93961f3baa9687592b26a3124658f33caacd8,16f1f5590dda0f160bd2be048475e1044ec23dc3eea59fa7ee4a6d6f611c6fa5,27131731af6af695c9c478d874005ed965976e83e35cc38471781ea35b8328b9,0900f21d0c7b4c6f849e80c4bcfea269997923c303d0a2ce9ffdc38a3967d3bf,22bc46f9247049ca04f6fa2ba14139a38c263b35a038070bdd3c72aaaf3d9140,1b7024e3b75ec359363e240d1842e39c8d5060cd90a45205480ccb78e956e0ad,02757fbc20386e4b59802d3c4963adc76ac9627e8a0067d7b04c4f5243373e50|2869eda0bdf2a4bbfdb5f38a6748a6fe6246647c383d4cf6b5c9e11854a66656,05d34c39c8cc334b2610867f9481d46b4e6f25b36a40f754eefe33d6ea648578,2e3a7d0f31df1a2de9b4a33ec568eecb65c8143c60645c903231545b0e2710c4,164102c1e33a2c54058e87bb2b41f5721f0eacd4a94526c674ccf8e546f30338,04ce48a25dd385f4698116bb18d0e024771cf99ae023bede5e22f423abc8750c,1545a9a2fd5e60e098166b4cb414b9c1103aef34c9c99198684590dfe4cedfee,0e39ade01a1a5a61d3f9e59a940494cf39fbed9dadc5fee3d67429ab6569ce0b,268e8a94115a2d5bf697202d40a650e490ac18c58f5301ce63a98f1acc20beab,09ea39fa387637eccff00db3a1763ac2ebd1fd2200d1c5f3675fddbf8ee6e6ce,0a2ad47858ff86a6f1703452776f952736a92c7fcb7d8e24971cc596f0d7ad72,11427d7f43d231df56896365ba33615df1fc592a42126b021bc81f7a21610428,0a48308275e27cf6ab3958e46cd51448b537ea448fbd9db9f81e6f3f011b46b9,1259bce188154b715d2b513134caa42c8944bd24d408d594a758127dc9d74a19,0f5164a99e2a77478ec936d66d0cba0ce1a08d38bc6e10e6ee65c51f8e699d4e,007cdf11bd3ba14a6f3a56b48c9ba63385db22782458523e710dc9e19f4f4d88,045c688900c20b6d44a8799f8bf4256c45bfc211784bc21ab2698a4c598fd3e7 +add|16|2df2b814bdb50682e00663ccd723624504fc160b72c64569bdfe25ac265f8b87,01d39840667efda722764c0e3c232c39ce99437169a9a0612880d367392f80aa,071e92bcb904417ea8bf884b8e40fa987e50a654e0f4e57a0bc7e336246f6b9e,21d3d07c39ca7dde40a70dc4d6f1cf140009c5b7776582d92b26bf1978aeafab,12eaa958a4064645b4f3ea092c24e7eec9cbfaa14d0f985d7ea14b8e8f4bc62f,1b767ea32dcda5e37f0ddecbf78a4dc4e50de4af192638cc0c55de4300f336a4,181a6dea8e589071456362d13d83d2cbec6e5e8327c814da500cde283570dac5,031eb006c71edbbf537de77ee054ec682ac8097436ae5cfb6d9f3a375a6daf16,132ebcd92ec1461a1da8242092b19a1cc5a41ecdfa0cb50353fd967708811969,03cde8c0a415986ababb06f87790923a233b9f7771a5693f5859d73134ad969c,0b782fc005080a85d027a31bf635edcba14217ce8a91edf7480a8ae94d0e5d6b,0624401e0e35d17faa1ba9a4406800c64fe5d6e5433becfe00339cc6fd6d0f66,0959c73a5b5aa530c7bb3a323b8df511814173eb3cf6a2da4e403e78249ddbe4,0cdb99d510ef76f5aa3645445ed87bd7b695cd532b9da9762592ca581ea07cb0,1fcd4a35d9ed073ad7b47334797d18bc61a4aa34abcf3a1302862e2b1b290e76,2168ac0135571c901e167f2b723cee92f5a6dcc5f4d7648597b7dc8811e9ea2c|205a8f23244e1f6698294149ecc45171c424eb99a082d65a42dc7a16e8fd0352,1387e9312ef4bb0f77e4769faf941ebbc248f5242b02b6c4f1cf6d3bd1cbde5b,2165fe88d3937f146622eb619e52efb555e49744d5a6d9d496c94945c786e81a,1c324d561762dd15df0c9edd92b5cdc578e1a037f946ebac748bb81674590bd9,06e30b907d5288c2948bee824b4568ea315b6a7e156fdcd569c2d76ceb399be5,2cacc853b22775f319ba32978b2f6ec04a1caabdf913cf63b50f23d966d232ba,1aad2cb19feda5ab09350f2b75daf5cd7f5b9dbad70b0ee86a601105d266e8b0,2f5e92af6ac1263b25aa373abe4204434cda5b8d2d2418d5e9fc762cbd3aab0e,1b3e274f78b47ac265f326d7491a0dc055d04d69a303ac41b9ffbc1aa245a6a0,18b0b3fe910134f3468c6c08e5a93961f3baa9687592b26a3124658f33caacd8,16f1f5590dda0f160bd2be048475e1044ec23dc3eea59fa7ee4a6d6f611c6fa5,27131731af6af695c9c478d874005ed965976e83e35cc38471781ea35b8328b9,0900f21d0c7b4c6f849e80c4bcfea269997923c303d0a2ce9ffdc38a3967d3bf,22bc46f9247049ca04f6fa2ba14139a38c263b35a038070bdd3c72aaaf3d9140,1b7024e3b75ec359363e240d1842e39c8d5060cd90a45205480ccb78e956e0ad,02757fbc20386e4b59802d3c4963adc76ac9627e8a0067d7b04c4f5243373e50|1de8f8c500d185bfbfdf5f6042665b59a0ed195c998fab32bcf8aa2f1f5c8ed8,155b81719573b8b69a5ac2adebb74af590e2389594ac57261a5040a30afb5f05,288491458c97c0930ee273ad2c93ea4dd4353d99b69bbf4ea2912c7bebf653b8,0da1cf5f6ffbbaca676366ebe826447c50b77da6f6f2fdf45bd0819bfd07bb83,19cdb4e92158cf08497fd88b776a50d8fb27651f627f7532e86422fb7a856214,17bef883fec37bace077cbad0138642806f6a7249880979e7d830c8877c5695d,02634c294d1495f296482c4631dd703c439613f58519b331768af99a17d7c374,0218f44350ae61d0c0d7d9031d15984e4f6e7cb8ea19054013b9bad027a85a23,2e6ce428a775c0dc839b4af7dbcba7dd1b746c379d1061450dfd5291aac6c009,1c7e9cbf3516cd5e014773015d39cb9c16f648dfe7381ba9897e3cc068784374,226a251912e2199bdbfa61207aabcecff004559279378d9f3654f858ae2acd10,2d37574fbda0c81573e0227cb4685f9fb57d45692698b08271abbb6a58f0381f,125ab95767d5f1a04c59baf6f88c977b1aba97ae40c745a8ee3e02025e05afa3,2f97e0ce355fc0bfaf2d3f700019b57b42bc0888cbd5b08202cf3d02cdde0df0,0ad920a6b01a2a6a55a2518b103ea3fbc6c122b9c2ba1b8706b10410147fef22,23de2bbd558f8adb7796ac67bba09c5a60703f447ed7cc5d48042bda5521287c +sub|16|2df2b814bdb50682e00663ccd723624504fc160b72c64569bdfe25ac265f8b87,01d39840667efda722764c0e3c232c39ce99437169a9a0612880d367392f80aa,071e92bcb904417ea8bf884b8e40fa987e50a654e0f4e57a0bc7e336246f6b9e,21d3d07c39ca7dde40a70dc4d6f1cf140009c5b7776582d92b26bf1978aeafab,12eaa958a4064645b4f3ea092c24e7eec9cbfaa14d0f985d7ea14b8e8f4bc62f,1b767ea32dcda5e37f0ddecbf78a4dc4e50de4af192638cc0c55de4300f336a4,181a6dea8e589071456362d13d83d2cbec6e5e8327c814da500cde283570dac5,031eb006c71edbbf537de77ee054ec682ac8097436ae5cfb6d9f3a375a6daf16,132ebcd92ec1461a1da8242092b19a1cc5a41ecdfa0cb50353fd967708811969,03cde8c0a415986ababb06f87790923a233b9f7771a5693f5859d73134ad969c,0b782fc005080a85d027a31bf635edcba14217ce8a91edf7480a8ae94d0e5d6b,0624401e0e35d17faa1ba9a4406800c64fe5d6e5433becfe00339cc6fd6d0f66,0959c73a5b5aa530c7bb3a323b8df511814173eb3cf6a2da4e403e78249ddbe4,0cdb99d510ef76f5aa3645445ed87bd7b695cd532b9da9762592ca581ea07cb0,1fcd4a35d9ed073ad7b47334797d18bc61a4aa34abcf3a1302862e2b1b290e76,2168ac0135571c901e167f2b723cee92f5a6dcc5f4d7648597b7dc8811e9ea2c|205a8f23244e1f6698294149ecc45171c424eb99a082d65a42dc7a16e8fd0352,1387e9312ef4bb0f77e4769faf941ebbc248f5242b02b6c4f1cf6d3bd1cbde5b,2165fe88d3937f146622eb619e52efb555e49744d5a6d9d496c94945c786e81a,1c324d561762dd15df0c9edd92b5cdc578e1a037f946ebac748bb81674590bd9,06e30b907d5288c2948bee824b4568ea315b6a7e156fdcd569c2d76ceb399be5,2cacc853b22775f319ba32978b2f6ec04a1caabdf913cf63b50f23d966d232ba,1aad2cb19feda5ab09350f2b75daf5cd7f5b9dbad70b0ee86a601105d266e8b0,2f5e92af6ac1263b25aa373abe4204434cda5b8d2d2418d5e9fc762cbd3aab0e,1b3e274f78b47ac265f326d7491a0dc055d04d69a303ac41b9ffbc1aa245a6a0,18b0b3fe910134f3468c6c08e5a93961f3baa9687592b26a3124658f33caacd8,16f1f5590dda0f160bd2be048475e1044ec23dc3eea59fa7ee4a6d6f611c6fa5,27131731af6af695c9c478d874005ed965976e83e35cc38471781ea35b8328b9,0900f21d0c7b4c6f849e80c4bcfea269997923c303d0a2ce9ffdc38a3967d3bf,22bc46f9247049ca04f6fa2ba14139a38c263b35a038070bdd3c72aaaf3d9140,1b7024e3b75ec359363e240d1842e39c8d5060cd90a45205480ccb78e956e0ad,02757fbc20386e4b59802d3c4963adc76ac9627e8a0067d7b04c4f5243373e50|0d9828f19966e71c47dd2282ea5f10d340d72a71d2436f0f7b21ab953d628835,1eaffd8218bbe2c162e21b250e1065db34843695b8605a2d7a935bbf5763a250,161ce2a6c6a26293faece2a0716f6340509ff75885077c36b8e08f844ce88385,05a183262267a0c8619a6ee7443c014e8728257f7e1e972cb69b07030455a3d2,0c079dc826b3bd832067fb86e0df7f0498709023379fbb8814de7421a4122a4a,1f2e04c25cd7d01a1da3f1eaeddc3761c325223999cbd9f99b28affd8a2103eb,2dd18fabcf9c8aeff47e995c492a355b9546a910ca767683298ec2b65309f216,04246bca3d8f55ade623f5faa39440820621962f8343b4b6c784b99e8d330409,2854e3fc973e6b81700542ffcb18e4b99807b9acd0c27952dddfcff0563b72ca,1b818334f44603a12c7ee0a61368b13557b4de5775cc27666b176735f0e2e9c5,24ea88d9d85f9b997ca52acdf34165247ab3c25315a5bee09da2130ddbf1edc7,0f75775f3ffc7b1398a776824de8fa4a128250a9d9989a0ad29d73b791e9e6ae,0058d51d4edf58c1431cb96d7e8f52a7e7c850283926000bae427aedeb360825,1a83a14ecdb0cd555d8f90cf3f189a9152a37a66051f12fb8c384d415f62eb71,045d2552228e43e1a1764f27613a351fd45449671b2ae80dba7962b231d22dc9,1ef32c45151eae44c49651ef28d940cb8add7a476ad6fcade76b8d35ceb2abdc +neg|16|2df2b814bdb50682e00663ccd723624504fc160b72c64569bdfe25ac265f8b87,01d39840667efda722764c0e3c232c39ce99437169a9a0612880d367392f80aa,071e92bcb904417ea8bf884b8e40fa987e50a654e0f4e57a0bc7e336246f6b9e,21d3d07c39ca7dde40a70dc4d6f1cf140009c5b7776582d92b26bf1978aeafab,12eaa958a4064645b4f3ea092c24e7eec9cbfaa14d0f985d7ea14b8e8f4bc62f,1b767ea32dcda5e37f0ddecbf78a4dc4e50de4af192638cc0c55de4300f336a4,181a6dea8e589071456362d13d83d2cbec6e5e8327c814da500cde283570dac5,031eb006c71edbbf537de77ee054ec682ac8097436ae5cfb6d9f3a375a6daf16,132ebcd92ec1461a1da8242092b19a1cc5a41ecdfa0cb50353fd967708811969,03cde8c0a415986ababb06f87790923a233b9f7771a5693f5859d73134ad969c,0b782fc005080a85d027a31bf635edcba14217ce8a91edf7480a8ae94d0e5d6b,0624401e0e35d17faa1ba9a4406800c64fe5d6e5433becfe00339cc6fd6d0f66,0959c73a5b5aa530c7bb3a323b8df511814173eb3cf6a2da4e403e78249ddbe4,0cdb99d510ef76f5aa3645445ed87bd7b695cd532b9da9762592ca581ea07cb0,1fcd4a35d9ed073ad7b47334797d18bc61a4aa34abcf3a1302862e2b1b290e76,2168ac0135571c901e167f2b723cee92f5a6dcc5f4d7648597b7dc8811e9ea2c|-|0271965e237c99a6d849e1e9aa5df6182337d23d06f32b2785e3cfe7c9a0747a,2e90b6327ab2a28295d9f9a8455e2c23599aa4d7100fd0301b61222cb6d07f57,2945bbb6282d5eab0f90bd6af3405dc4a9e341f398c48b17381a125dcb909463,0e907df6a767224b77a937f1aa8f8949282a22910253edb818bb367a77515056,1d79a51a3d2b59e4035c5bad555c706e5e67eda72ca9d833c540aa0560b439d2,14edcfcfb363fa46394266ea89f70a9843260399609337c5378c1750ef0cc95d,1849e08852d90fb872ece2e543fd85913bc589c551f15bb6f3d5176bba8f253c,2d459e6c1a12c46a64d25e37a12c6bf4fd6bded4430b1395d642bb5c959250eb,1d359199b2705a0f9aa82195eecfbe40628fc97a7facbb8defe45f1ce77ee698,2c9665b23d1c07befd953ebe09f0c62304f848d108140751eb881e62bb526965,24ec1eb2dc2995a3e828a29a8b4b6a9186f1d079ef278299fbd76aaaa2f1a296,2a400e54d2fbceaa0e349c1241195796d84e1163367d839343ae58ccf292f09b,270a873885d6faf8f0950b8445f3634ba6f2745d3cc2cdb6f5a1b71bcb62241d,2388b49dd04229340e1a007222a8dc85719e1af54e1bc71b1e4f2b3bd15f8351,1097043d074498eee09bd28208043fa0c68f3e13cdea367e415bc768d4d6f18b,0efba271abda83999a39c68b0f4469ca328d0b8284e20c0bac2a190bde1615d5 +mul|64|08e18f65b91169caa761bd3216d7ac010352a041cf83d94441cd01efb7efc041,05c905a68b638bd30bb06ac0cd844982dac3c337d0d6479c38595a4641491f2c,067b4fc60bed16a909bca1ef3dc04a48880f602bb016a7985740d3d275bbaa06,13e77369d11526df2e0dc9aa0b1468ee5b7ce35e038eb8b02d6aad3e06bfb47d,1fe77bfd637f9f09114cc41f40fecfda68be042ec6ea9a6a59f9846a16ffec06,23827edbc1cfc456261458dbbfc37a108c637f5e60eaf69064a4af6c393048d1,178d19a786f3b4f1b9df772effaecfece39aab6ddb687845b8d4d46468b97e5e,2bf335a1dba54a4fb5b02259c9a06c11a9bb014b477df2e1955290f9dec24117,2186d955510817bcceabe943924ea3a8995094094ad0a5052e12e6a81650c0bf,07eae1dd3beb9921bdb8613737b2ad2050906ddfd6131c4e06604d1c28dbaa4b,112b8c907027e45bb61367766c4ef5a80022210ae76637040564890ff60bd6cd,19eb2f05eaede9bae5a9331a99a8b621d937777a6168201fd6d192cd7e0fb3d6,1a359e5f42a0a18bfb09d2a7642a645cc7e617843c515d249ff16c133da392b9,22d922871eef34cf8ebc9f31133b2c44b074c8e5195abe7242c7655b076946a8,00a06d60714522392477280015af9e5ec51d70125d06e25b089d96b1a349f9d0,18e6002e1649da58cee1fc73234387de1e9acad0972cca1994122eb9967a4bc5,0a5a3ceff59c63b1b14a2d4ca6c3ec1ae3f56d63e62228f10224f375d46edc66,05f6f2f0ac035357c5df93986f55cafe6b63a198ce3583f29ccb5a8fb635a068,2991b41a2f8054938301c8a3568a48fe849715f041997b99be6572ebf98d8152,06f3fff25f0cc2efdfce6624933577ae2f444d013252ac18a18e022c6a080989,12ac5dd14b7ec7699132723d51f0918d21cfba315b8745b22f15db3f36e59c31,008fec4dc13338f27a2c4a1cd803d3eae840592419e244db3e6b815c7eecd3cb,019a227716914afd7713f2a408f9166c100ab5bb5a5f2edf3a4e2a5f06278d4e,00219941f5eae408312a1ee9ceedd637566e48bf6d59c90abd42983a76f236ce,0f9725d62b9605989ce30cf476da05b62eae08e8193b41d73afeecf864d2364a,066031bb4be3018dfc9b7ff36911172a63b420730faeca4ac3ffd49234ea0a98,2c019561d60c12ac62f1f41d0fd4da04984035fd09a60e69d4e7b441ba70f3a0,266ec1cf74f68b8ebaeab4881f6f991e142ce113b31dc5da037106a9a0d8ac8c,0d732a8f831451d78f0df110316cf1d7e99105450af5d0abf43d900d92961fec,119446de68dcbc090614b86c58531ddac817e9a6e62283210f18e895ff0f4835,0d1b55e07ec1c1a6eba0735b71d54966a15a6be39705ee69ef4a83e5cb49db8c,20be6a4b85ed80fe52922fbb5c1d090c854738cdd5c84b948a024af0757d2df5,1d7daac1c230c9bac495a3a5e345055f962d75e3dd1d7a33bfd849888c61c5c5,2d718356213fb130a67de0d0bdbd78342d9347782e1445ad59a6eb124ff6e850,041dc2fe70b8b41b0a017676673da8901d26ef65243f72a6331a55074c3438bc,0483ff24387e8d31f65cc73701625647b2517b64d977cefc1a948ca5e4be45a6,3039410c7af0fc2dda75761337f477653b575c914b67a517b6c0f255ef0456c7,1654173bec613e3cd4127af606926d4c7c1964a70c229a138548f2a2854a614e,2486eb178da4c9f928e033175b113acf6e339d8d94cb1a2a9f4c04009a0b9dfe,28991a3a92d69c54d206eb141fbd070259ea34dfa5accdb70f2527cfd69cdfc7,06ad429b49b8d169488d0ac8721686af12aa965b94a3d27f75b4317c7a952435,1dce12c81fdc1decd362f2125a1eba528a46586a0f012040a9871258a27f7531,1cecdddd43af38a2085e23d027945dd3088538b22db13122b0e13b11d7795395,21d91dc400c75749a16334b0d5a80e45f9eb2184ff9d5e9f95dfe0a306186bd4,263f585b355b64d9d4f2823a7bbfbdb7716c1b727a838e40e73d2ed96d70b2a3,18e31747e101b21e899762c7e72535792b36c054baa43ebe386c0d7f90e58387,2dfaab0e42ac3d29d272a9daa4d1ad749b3a5a3dc6487cf85d763dbcc2e41654,0af0949b2e5ffacb483977d61dec3007097f9b82963697e68f01653c1cb94052,01c44cf0fb267eb34a349e93fd9008a169264b3641361766dde8d6c98152980c,28d57fb38272af71418968464958aee0eff79e083cd207d6fb12f148dae5110e,2f70b3c9a91dd08035404195147364cf71e0b3f903e75dcdd9aa742da165b895,2a8bb75d4a4d64a766bc67a58fdcabee4c67ebf55a0bf21041347223f070bb06,188fe5bd5e738c589c8c1d2ba38551f05ea906271b3728c77a4f2ebba8202dc4,1721a1047915e206909a53d2ed98d929759ab0d19091cb8c6a9714f586152801,2e5a5b14a67df128e31943b6f5ad3459292c3dd3eaef678129c821182e526051,1846ffa436ebb77a03d3e48f72e17eb85117d704894bed8b385c002f6308d2d2,098f359747675d562bd2b7344e399bea020643d9f1fe81516d629ad27905e9b9,0ce18a33862d3071c046c9882785da061b539d2b04f4b2535f71c4d8948fc624,1e3e714b236edf292764c86d3b6cd6395189cb42d771757bc9139655ab45d1da,04660d1d9270afbf3a34024958138513f8ffc7dc5eb012de8cd364ad3e5d20f2,1af2b683b29a7dfea6056588d3087741cf927ac9995d8e2050c0f15707e6363c,1509bbc931ddbcc9eb15ef333d9e0735be03305894b87f32a4cb77149bbd2601,2b3dccc00eed8319a9091f75a8090fefcc7059f6c698894951f664cdc53a03e5,03572b106830187278c625444959981edbec3ec42fcddf1ae1f97f0fc450fcb9|2bc6453a148b9b40181c97049a146619baea7ada57695078fb93734427488fa1,2180636d5efbf81b5de925e450dd408c931b078450d75c752b28c5fa4d64b478,229603124923b4de550c627c038797f248b66bfab09c8952b0005132f00b9be5,2c26a6e64443087404b76ae5243b85e40ca799412abf203fcac90e490ee0f2fa,218e4c52cd26e410b62468c7eabeccbb4a62e01164d167d34b775f60aecc3031,1e45f1422144347185569c95d167caf3c849401de2c7e184ae7a0b5401643bc8,0ec9f02eebacea2517e2ff246796375caa11d2290a1f986c2f0a811d319ec425,0cafc60e89694bffb3248ddf683cea8b9c30c17bc9dfdc04e436729ec70166db,0c8beea842d2b0f6316e7631e010944037fec239d8ec635dd22cd09a12181669,2849584679df81d3f4546e17fc960ce8d058267fcf9318a5d40b3a2e0e53bdc3,28d99f5b94409dc0e204796fa8c0675d84c8e79553fdd218bcb1cfae97782b2b,28725ed3abf094f81e6c2f2a0cedaf3782841e3b5f12ed9b0a3a20c2d373c114,2122c3dea1a171fbe0c314a191278d7d79879388780dc69e4f5bfa156bfb7d1f,01b609ff3be168fd6d9d6428f07f3af6341f5d2f8a28b5eb508b6d00595abdb2,1ecdbe667fae1a800079887ab996a22a3b047d7fb9f410051cbf6a2de51fb357,0f33a882d555f9b2d624466b9658930863d11ad2a63accaa2e9ad7c184e74fbb,02a4a4f011936088c435916341cfdb09107fbd5150778b4258402be0969ae2c2,24b532d4a55f95c9d79d5cb1411aca4a04e4eb36d5f076f3483c3ad5036c0309,0bc52a9227a8eaf64f876eaffbcc156342f523534bac76f9a3593251200357f9,288116a669c60d9c86f89f43b4ee5f1c0c6f7846907b41107108f34b15470261,08528024b99f04c50493830871a19f4e12c9634a6057e54b3b37109755aa8272,1de72bdd86224d71732bb770d23c531beaec70dbf79ca02a47b3db89707ee0cd,0017bfc85cf34629600958fbaa66051e3c663b57f01390c6107eac5c1d327d8c,2dc6ddd6b1b58b3b276009c4df38890ee3d22607245767d44e9e20b35d12f331,1b07c057b9886004e9559cbbd379939b27f83003cc7e10a5d3a783b33d47fe1e,0b9c9badcbc14912f2d44aa75f560974ee69fca6995470ac3905c6b3f209a6c2,1f2e9b3e702ec0248bd2efb0d109345f8c14b2581ff58de6b9a51dea08fdee11,22971a9bb18b9a4dc3a4c7bae3b9c58fad3da8addd0c692c8254b56e3a16718a,17b52c27b747f82d62668622cd387995cad30fb0ebd2ad5af24fad2abd718295,104d1c2b70c6f72be8c0925010baef9953aeb80d5eb9f8a04da640d2d48211e9,1b9586eaa646e95013713bbd50ec45c2f541ed7402e1037ea4f3dc41716948af,2de9e58649b9959730ffc760d7ffc27a5343e4dcdaa60ec48da0ea644a27937c,239269989ad6f3fcb5768488e6c84b25a0a11f464501bcfe23b0560e18fff191,0758083f1b22bded9c55a84c22a0e0db00b38fd5d0a61dff2dc5a68f896e0f1b,08fa64a29aa9164a744af47ede21f8177253018136c6e239be1b8dbb56f4bfcd,21e35081d92d5bbf2c71319f6ace65f13db75ee66e4d49cec3e0ccdab30d7d93,277356f4c80098fa2a63dd988173935ec739d25bcfbd75ae2ac474eab73fdb56,21aee89b6a99bcf34a8267128e7d3af7e1c5656ed3b35d83971060321c1dfa84,2cc87ea5f07cef57e5c5d72cc2bcb717aa62ca5ea49a4160d0c804aa24286629,2a9b6facd1cc895862a9555193f20a8b77aac49bc2f867f2e5351db3029ccf89,1c5fcdb15a0733152bd47663d687d121ef64a9b9cfc9398e524c1010bd11f3af,01e75e0c9a5e8c823d5bf6283f11d79a309ed1ff4deba681f8423cc4217d4939,302c137c9c46029491425553c48559d44dbd763834361f9b58cb0edc4dc59923,2a1c3e619c095f9b82cd6fb5bb49fbb72297a02c7cbac8ecb6270360712c4bd0,0b69e3da58de74b6d0de318dd978263cacd7608587176835e75bc250f6e45100,224b338a3ef4de5770bd1eea40c0c7fc25b049464c4865eafd68a0472f0446c3,08d52bc0d973cac47a0b6364c4653f2fb7d9cde59fb7cac79b14478aa1fca776,09eabe6e3fd88b3716c3e694998a7d7da1ca931f589844f378b30e68f63503b5,0926b4d5478d057cb5600069994066c33e48aca889cefe990a62c0d996aba3f6,0899c306592e87d4fa87d2952d9964be8a276e7d694c9a2ee9b4c56a9156e3a7,1f3dad6f39de95a7d19ca841b88d200b7a6544fc2e8cf3eb44d32d309b0fbc5c,1b7a3500e475f06a024a34af6959d26306887f640923a6efb3c004d7f2c4d503,22131bf7ebd3be91652a969d80dafeaa70d1afa51459316064673320f7e1ee36,00cca0f3ed5ad11cfdcd5fc069efc02c2158cfb2c9951e409702aec125787533,14a47d79a4619b6cfede2729bf0ded93334f5236d77fa4ebf285cea26de3bd0a,275b9f85d4ab5a4a4d157d24db24b76492ed16f8d8b3243a4589032ec587e7e9,1847bd4dfde8d29be9a64c48dfe08bb110135d73990b91ab93ce0e4ad68b7810,161e2786b645376392c2b593140753e52a2b0345ddd0413d795e4e60865152b4,1f66d6fc1428f29b4debf9392bb953e93ca5e7fc1582b759797329d5309761a8,19d0857dd3be6ace2d30fb9194d3c237c18c3607f26a7ae129b0fc6195706e76,0b84e5e594a40ce246520abca12ebb89281f081e4d74a825385a0967a692f4cb,285466801e78c555bbcfa2f3fea6330044caaaf0672cf5da54d1c9dba92d62b0,09287e02ba46da8f45591cd4b7490a3f33b7eb9e240b3419244bf52df11f836e,2a7b42273ec3515f7363ff7fecd14f6c0e1d425f6fa9e45c5543d924576b628f|1070d9794b4184f7d04168436133060357630e5499814e5113371d9291677205,29145860b1397708ea785d47acc0718508c716dbd4d34a8757fd922fae88d151,2d097f6f7f06d0b264f4ac1357bc7aa1cd0d8f7c931c1043ca3e732e9d55dc7b,0314372daba464fb2b1266aabf99229d959e6dd17db436a97b86c167f2b180f1,0f6f4e32e4d99fbb7d96f75b6d46ed94d7068d0a1d4c0732a592c4e9d1027102,1db2d7d2da2871414ecceea82f92479f8d27be580c48c4daf5fa9fc6c26cdb3d,1ca4c1ab1e4bf59f01d4870d8d6afec416e8d8d31d6979e598af23ce60aa5d78,2026ab265c7beb4929c1c2cdbffdf749f79eccf310e5230814deb488c0427a1b,15341f767e66aeb6711cd77337d970b74407bd4f02613a1802ac570e271cef97,29d79ea60db83e10ead8f69497317002b87c0793a6388504115a0a41262b500f,2e8f9f8ed66a5de0edef89f9e7e0c209ca8110b6ebf17840870ebf358d1829ba,2f30ce923b03fe16abff9a5fb56e8301fdd021e25b4d47fd29d5bd117c03404a,2cf0988a2565df2104d1e124a8465f12574d52c1ada5cf97181455a7e09d7dbe,2744c99925d1b4d8f2407b1b39c6823d9abac5136e31dbd3971ef517d98838e7,2652dac8c93509fbfe59c9a485132fbf6c3d7370ebf4c00ec7617c9dc99545db,056ea9fe426f6dafffc87cc8af7fb408d88585610ded54508a3533ac2fbffc50,21a1470389986ab616ef09f62e9fa0d950c8dcc2b503b225b0e42e538683b299,03b02f2fed4dd44be4b93df9c5d2fe44352c76c08a9c06b5915953934e0117c6,2cbe2b2cab6b466aaecc1c7ffed24355f52708ae141b91d18d7ed12a8de16132,035c9393ddbe1f99a72c9b06adfa1b3b3578c70ac63f76169038c4ff1299aa3e,072668c8b5fc6b81be46e0aa6c275f23124cfd32af4c699d15339dd29027442a,2838251623d1676b39bb9c1db68256d6a1fc25e686b2f75e8c3bd00e466437c0,01e8c3752b1a415f084b9e728d281197ca9ae03a648b22739f40ae7921335246,278500ac1a536af0273e869aed8a8b10002e40aefc4b7598455d1e538123e08f,0674bf37d300cc4d571a3a1d31227ec61f1ee853924481246d03c35acac5ee15,048f57cce92ea8d61f38acb1005d020ec03286e25cd1a8ca9cbbb2b0d74e23d9,15bb60f4b4a89187fb7fd900396e44adb2ed3d943555c4b480fedee13ec200f4,01fddebd249fe3a53b395f288afbe8839f808e32b284e3bc2f70098deb6bb000,084cd96b797dadc991cbeaf7a7129c27480b20dc912f4f9ac0b5474d9d8bac95,1b1b9b8984a2736ee1bf3ead8e7a7414b3bc0c91c8f17feb1f0f7cb4806eec05,07ebbd10e5c453ef37f3802d7cdcf9dede84ceab4b915cb273835f7d7b02b0cb,1f8eed4f071a6264159d47caa211628f6e10b8e790dd4d8a8ca9fed5a0fdca17,27189d1e753eeda76b779da4d6cfce0c0b9fab9a4b7adbc5f05d7b2fb87397c8,103f443794d4614414a9b0ec2425ea95f4d59c7aeef6bf6ae5cdc9e0455c46c1,2989eeefbf595e333551d2f3c26e68df59fe13e56b7bdbb8b6b439c1aed363fc,1f708d2bd17fa9ddaa38b9d56689e167eb3018ed312690eb6ea15b338880836b,0a2472f5319a93f32640c2c5246dfc3fc554dbb49e6a1357ac1248d6d4615057,19a6c0751f98786d03b220c6b057b90f1517cf67102fb8404f247cb70235c42e,231a0b971832e3db123cc57ecbe6dd644104787e4b21b53e6bac492096381ea0,1b6be940ad4f7c282010a8bc46d8aa06cc64870525805d8079bd57853cba9b9d,18aafeaeb8742f6f183720912e1a5e5a8ee6d399e80dd352514ca6c40cab3948,159ed8b88ef097fdda32dc5195fa949ab24bace06449a71bf83ee26cf5f41334,1357cec42e794140a98d10d9c51569d9d4662d5784b83b7e52353e3b60ebb216,118a228dee639f7e2a3fa248510bdb7b11afa158e15d13ef6647a8d0551c3d8e,1c3f0c1902bb36c75df121d130896f718929d7d160d214d3f0759ecd6aaa75d1,0b7e5f29fa85730dd74df28bb70872303729d9df93d39e1ffe7cc0446398ade3,1639f03dc1f6afdd8c6452d3f885230b1b9972a1be6e6437593260567596dd7f,02615f9d79ba835e8968b14b3c5836e44e24ef6dac3d96dc3e2bf6b694aab28e,2585e4b24906477df8327d0be16f54ded6f40287136f2a9ac4abe0462e65b992,1b466d38b94e3de4e00c96541d59dc8d55d333f37a91c85072b6727b6e55818a,1457ded8ee1ac1d70dcf98016346d0032d12b4a35e3f87c162f7f790bf2e1d42,200ee59c8539f38e0a9466c117e861a8b305263ce0072a1c9cef7c496612dc1e,0b537b83ef339c8f7bf653f728e54c52e4b75e853a87c7295ca134d49d7bab22,078cade94f5ef7ddb1ae65d11ce3bdb5b246b7734d11c033a159e919c2aae8d3,0fda188c48e326735e385166555ae40736ac124c4c2d7d284d8cd1fcf606be4e,030cab0f8641a1f26fbd30b3d5b5ca9dd66cefc13b8c69e057cb8c8ed60b0b08,020580618ac3f849b402e80fa6e3e240d4e8a6301d50251297837d717f882e48,2e7c86970886200e343d7725116f3e9eaae2c224e9f46e846687882955dae549,234ab23e58437a3801ae530192883b72c6b098390bc3238eda870b9a8a888b9a,11497df70b9ee3dce2d60ae62d84c57b8302bde8912c264ef148db969918119e,1c55e85485d0a93eade034f2bc3fc4c25b1b9b228de0f0cc2c1965350955bd13,11cd34fa7dfe4956468255c97d3befc8dd68eb13097712435b1ded3422439697,122ac73d0feb1a7128f8a487df70da61f6f204d2e4dff3abbbdb0bf4a06d982d,1e3de1ea3dc3ec3f3d6c44ad4e88986d968cf1fe7cb924ce945b1539d9048da6 +add|64|08e18f65b91169caa761bd3216d7ac010352a041cf83d94441cd01efb7efc041,05c905a68b638bd30bb06ac0cd844982dac3c337d0d6479c38595a4641491f2c,067b4fc60bed16a909bca1ef3dc04a48880f602bb016a7985740d3d275bbaa06,13e77369d11526df2e0dc9aa0b1468ee5b7ce35e038eb8b02d6aad3e06bfb47d,1fe77bfd637f9f09114cc41f40fecfda68be042ec6ea9a6a59f9846a16ffec06,23827edbc1cfc456261458dbbfc37a108c637f5e60eaf69064a4af6c393048d1,178d19a786f3b4f1b9df772effaecfece39aab6ddb687845b8d4d46468b97e5e,2bf335a1dba54a4fb5b02259c9a06c11a9bb014b477df2e1955290f9dec24117,2186d955510817bcceabe943924ea3a8995094094ad0a5052e12e6a81650c0bf,07eae1dd3beb9921bdb8613737b2ad2050906ddfd6131c4e06604d1c28dbaa4b,112b8c907027e45bb61367766c4ef5a80022210ae76637040564890ff60bd6cd,19eb2f05eaede9bae5a9331a99a8b621d937777a6168201fd6d192cd7e0fb3d6,1a359e5f42a0a18bfb09d2a7642a645cc7e617843c515d249ff16c133da392b9,22d922871eef34cf8ebc9f31133b2c44b074c8e5195abe7242c7655b076946a8,00a06d60714522392477280015af9e5ec51d70125d06e25b089d96b1a349f9d0,18e6002e1649da58cee1fc73234387de1e9acad0972cca1994122eb9967a4bc5,0a5a3ceff59c63b1b14a2d4ca6c3ec1ae3f56d63e62228f10224f375d46edc66,05f6f2f0ac035357c5df93986f55cafe6b63a198ce3583f29ccb5a8fb635a068,2991b41a2f8054938301c8a3568a48fe849715f041997b99be6572ebf98d8152,06f3fff25f0cc2efdfce6624933577ae2f444d013252ac18a18e022c6a080989,12ac5dd14b7ec7699132723d51f0918d21cfba315b8745b22f15db3f36e59c31,008fec4dc13338f27a2c4a1cd803d3eae840592419e244db3e6b815c7eecd3cb,019a227716914afd7713f2a408f9166c100ab5bb5a5f2edf3a4e2a5f06278d4e,00219941f5eae408312a1ee9ceedd637566e48bf6d59c90abd42983a76f236ce,0f9725d62b9605989ce30cf476da05b62eae08e8193b41d73afeecf864d2364a,066031bb4be3018dfc9b7ff36911172a63b420730faeca4ac3ffd49234ea0a98,2c019561d60c12ac62f1f41d0fd4da04984035fd09a60e69d4e7b441ba70f3a0,266ec1cf74f68b8ebaeab4881f6f991e142ce113b31dc5da037106a9a0d8ac8c,0d732a8f831451d78f0df110316cf1d7e99105450af5d0abf43d900d92961fec,119446de68dcbc090614b86c58531ddac817e9a6e62283210f18e895ff0f4835,0d1b55e07ec1c1a6eba0735b71d54966a15a6be39705ee69ef4a83e5cb49db8c,20be6a4b85ed80fe52922fbb5c1d090c854738cdd5c84b948a024af0757d2df5,1d7daac1c230c9bac495a3a5e345055f962d75e3dd1d7a33bfd849888c61c5c5,2d718356213fb130a67de0d0bdbd78342d9347782e1445ad59a6eb124ff6e850,041dc2fe70b8b41b0a017676673da8901d26ef65243f72a6331a55074c3438bc,0483ff24387e8d31f65cc73701625647b2517b64d977cefc1a948ca5e4be45a6,3039410c7af0fc2dda75761337f477653b575c914b67a517b6c0f255ef0456c7,1654173bec613e3cd4127af606926d4c7c1964a70c229a138548f2a2854a614e,2486eb178da4c9f928e033175b113acf6e339d8d94cb1a2a9f4c04009a0b9dfe,28991a3a92d69c54d206eb141fbd070259ea34dfa5accdb70f2527cfd69cdfc7,06ad429b49b8d169488d0ac8721686af12aa965b94a3d27f75b4317c7a952435,1dce12c81fdc1decd362f2125a1eba528a46586a0f012040a9871258a27f7531,1cecdddd43af38a2085e23d027945dd3088538b22db13122b0e13b11d7795395,21d91dc400c75749a16334b0d5a80e45f9eb2184ff9d5e9f95dfe0a306186bd4,263f585b355b64d9d4f2823a7bbfbdb7716c1b727a838e40e73d2ed96d70b2a3,18e31747e101b21e899762c7e72535792b36c054baa43ebe386c0d7f90e58387,2dfaab0e42ac3d29d272a9daa4d1ad749b3a5a3dc6487cf85d763dbcc2e41654,0af0949b2e5ffacb483977d61dec3007097f9b82963697e68f01653c1cb94052,01c44cf0fb267eb34a349e93fd9008a169264b3641361766dde8d6c98152980c,28d57fb38272af71418968464958aee0eff79e083cd207d6fb12f148dae5110e,2f70b3c9a91dd08035404195147364cf71e0b3f903e75dcdd9aa742da165b895,2a8bb75d4a4d64a766bc67a58fdcabee4c67ebf55a0bf21041347223f070bb06,188fe5bd5e738c589c8c1d2ba38551f05ea906271b3728c77a4f2ebba8202dc4,1721a1047915e206909a53d2ed98d929759ab0d19091cb8c6a9714f586152801,2e5a5b14a67df128e31943b6f5ad3459292c3dd3eaef678129c821182e526051,1846ffa436ebb77a03d3e48f72e17eb85117d704894bed8b385c002f6308d2d2,098f359747675d562bd2b7344e399bea020643d9f1fe81516d629ad27905e9b9,0ce18a33862d3071c046c9882785da061b539d2b04f4b2535f71c4d8948fc624,1e3e714b236edf292764c86d3b6cd6395189cb42d771757bc9139655ab45d1da,04660d1d9270afbf3a34024958138513f8ffc7dc5eb012de8cd364ad3e5d20f2,1af2b683b29a7dfea6056588d3087741cf927ac9995d8e2050c0f15707e6363c,1509bbc931ddbcc9eb15ef333d9e0735be03305894b87f32a4cb77149bbd2601,2b3dccc00eed8319a9091f75a8090fefcc7059f6c698894951f664cdc53a03e5,03572b106830187278c625444959981edbec3ec42fcddf1ae1f97f0fc450fcb9|2bc6453a148b9b40181c97049a146619baea7ada57695078fb93734427488fa1,2180636d5efbf81b5de925e450dd408c931b078450d75c752b28c5fa4d64b478,229603124923b4de550c627c038797f248b66bfab09c8952b0005132f00b9be5,2c26a6e64443087404b76ae5243b85e40ca799412abf203fcac90e490ee0f2fa,218e4c52cd26e410b62468c7eabeccbb4a62e01164d167d34b775f60aecc3031,1e45f1422144347185569c95d167caf3c849401de2c7e184ae7a0b5401643bc8,0ec9f02eebacea2517e2ff246796375caa11d2290a1f986c2f0a811d319ec425,0cafc60e89694bffb3248ddf683cea8b9c30c17bc9dfdc04e436729ec70166db,0c8beea842d2b0f6316e7631e010944037fec239d8ec635dd22cd09a12181669,2849584679df81d3f4546e17fc960ce8d058267fcf9318a5d40b3a2e0e53bdc3,28d99f5b94409dc0e204796fa8c0675d84c8e79553fdd218bcb1cfae97782b2b,28725ed3abf094f81e6c2f2a0cedaf3782841e3b5f12ed9b0a3a20c2d373c114,2122c3dea1a171fbe0c314a191278d7d79879388780dc69e4f5bfa156bfb7d1f,01b609ff3be168fd6d9d6428f07f3af6341f5d2f8a28b5eb508b6d00595abdb2,1ecdbe667fae1a800079887ab996a22a3b047d7fb9f410051cbf6a2de51fb357,0f33a882d555f9b2d624466b9658930863d11ad2a63accaa2e9ad7c184e74fbb,02a4a4f011936088c435916341cfdb09107fbd5150778b4258402be0969ae2c2,24b532d4a55f95c9d79d5cb1411aca4a04e4eb36d5f076f3483c3ad5036c0309,0bc52a9227a8eaf64f876eaffbcc156342f523534bac76f9a3593251200357f9,288116a669c60d9c86f89f43b4ee5f1c0c6f7846907b41107108f34b15470261,08528024b99f04c50493830871a19f4e12c9634a6057e54b3b37109755aa8272,1de72bdd86224d71732bb770d23c531beaec70dbf79ca02a47b3db89707ee0cd,0017bfc85cf34629600958fbaa66051e3c663b57f01390c6107eac5c1d327d8c,2dc6ddd6b1b58b3b276009c4df38890ee3d22607245767d44e9e20b35d12f331,1b07c057b9886004e9559cbbd379939b27f83003cc7e10a5d3a783b33d47fe1e,0b9c9badcbc14912f2d44aa75f560974ee69fca6995470ac3905c6b3f209a6c2,1f2e9b3e702ec0248bd2efb0d109345f8c14b2581ff58de6b9a51dea08fdee11,22971a9bb18b9a4dc3a4c7bae3b9c58fad3da8addd0c692c8254b56e3a16718a,17b52c27b747f82d62668622cd387995cad30fb0ebd2ad5af24fad2abd718295,104d1c2b70c6f72be8c0925010baef9953aeb80d5eb9f8a04da640d2d48211e9,1b9586eaa646e95013713bbd50ec45c2f541ed7402e1037ea4f3dc41716948af,2de9e58649b9959730ffc760d7ffc27a5343e4dcdaa60ec48da0ea644a27937c,239269989ad6f3fcb5768488e6c84b25a0a11f464501bcfe23b0560e18fff191,0758083f1b22bded9c55a84c22a0e0db00b38fd5d0a61dff2dc5a68f896e0f1b,08fa64a29aa9164a744af47ede21f8177253018136c6e239be1b8dbb56f4bfcd,21e35081d92d5bbf2c71319f6ace65f13db75ee66e4d49cec3e0ccdab30d7d93,277356f4c80098fa2a63dd988173935ec739d25bcfbd75ae2ac474eab73fdb56,21aee89b6a99bcf34a8267128e7d3af7e1c5656ed3b35d83971060321c1dfa84,2cc87ea5f07cef57e5c5d72cc2bcb717aa62ca5ea49a4160d0c804aa24286629,2a9b6facd1cc895862a9555193f20a8b77aac49bc2f867f2e5351db3029ccf89,1c5fcdb15a0733152bd47663d687d121ef64a9b9cfc9398e524c1010bd11f3af,01e75e0c9a5e8c823d5bf6283f11d79a309ed1ff4deba681f8423cc4217d4939,302c137c9c46029491425553c48559d44dbd763834361f9b58cb0edc4dc59923,2a1c3e619c095f9b82cd6fb5bb49fbb72297a02c7cbac8ecb6270360712c4bd0,0b69e3da58de74b6d0de318dd978263cacd7608587176835e75bc250f6e45100,224b338a3ef4de5770bd1eea40c0c7fc25b049464c4865eafd68a0472f0446c3,08d52bc0d973cac47a0b6364c4653f2fb7d9cde59fb7cac79b14478aa1fca776,09eabe6e3fd88b3716c3e694998a7d7da1ca931f589844f378b30e68f63503b5,0926b4d5478d057cb5600069994066c33e48aca889cefe990a62c0d996aba3f6,0899c306592e87d4fa87d2952d9964be8a276e7d694c9a2ee9b4c56a9156e3a7,1f3dad6f39de95a7d19ca841b88d200b7a6544fc2e8cf3eb44d32d309b0fbc5c,1b7a3500e475f06a024a34af6959d26306887f640923a6efb3c004d7f2c4d503,22131bf7ebd3be91652a969d80dafeaa70d1afa51459316064673320f7e1ee36,00cca0f3ed5ad11cfdcd5fc069efc02c2158cfb2c9951e409702aec125787533,14a47d79a4619b6cfede2729bf0ded93334f5236d77fa4ebf285cea26de3bd0a,275b9f85d4ab5a4a4d157d24db24b76492ed16f8d8b3243a4589032ec587e7e9,1847bd4dfde8d29be9a64c48dfe08bb110135d73990b91ab93ce0e4ad68b7810,161e2786b645376392c2b593140753e52a2b0345ddd0413d795e4e60865152b4,1f66d6fc1428f29b4debf9392bb953e93ca5e7fc1582b759797329d5309761a8,19d0857dd3be6ace2d30fb9194d3c237c18c3607f26a7ae129b0fc6195706e76,0b84e5e594a40ce246520abca12ebb89281f081e4d74a825385a0967a692f4cb,285466801e78c555bbcfa2f3fea6330044caaaf0672cf5da54d1c9dba92d62b0,09287e02ba46da8f45591cd4b7490a3f33b7eb9e240b3419244bf52df11f836e,2a7b42273ec3515f7363ff7fecd14f6c0e1d425f6fa9e45c5543d924576b628f|0443862cec6b64e1072e0e802f6ab9bd960932d3ad33b92bf97e7f9fef384fe1,27496913ea5f83ee699990a51e618a0f6ddecabc21ada411638220408eadd3a4,291152d85510cb875ec9046b4147e23ad0c5cc2660b330eb0741250565c745eb,0fa9cbdd34268f297a74eed8adce96753ff09456b494685eb451c5f325a0a776,111179dd4f74e2f00f20e730aa3c44388aecfbf7b20291ac618eee36d5cc1c36,116421ab01e2589df31aafbb0fa9eca72c78d733c9f96783cf3cc52c4a948498,265709d672a09f16d1c27653674507498dac7d96e58810b1e7df55819a584283,083ead3d83dcf625b0846a82b05bfe401db7da7e97a45e5535a70e04b5c3a7f1,2e12c7fd93dac8b3001a5f75725f37e8d14f564323bd0863003fb7422868d728,30343a23b5cb1af5b20ccf4f3448ba0920e8945fa5a634f3da6b874a372f680e,09a0dd792336e1f2dfc79b2f938e04a85cb72057c1aa988b7e34632a9d8401f7,11f93f66b5acde894bc51c8e25150cfc3387ad6d46c19d299d29bdfc618374e9,0af413cb0310735e237ca19273d0997d1939c2c43aa5b331ab6b7094b99f0fd7,248f2c865ad09dccfc5a035a03ba673ae4942614a383745d9352d25b60c4045a,1f6e2bc6f0f33cb924f0b07acf4640890021ed9216faf260255d00df8869ad27,2819a8b0eb9fd40ba50642deb99c1ae6826be5a33d6796c3c2ad067b1b619b80,0cfee1e0072fc43a757fbeafe893c723f4752ab53699b4335a651f566b09bf28,2aac25c55162e9219d7cf049b070954870488ccfa425fae5e5079564b9a1a371,04f2903975f79f601a38f19cd0d506049f5850fb138c82021ddcafa92990d94a,2f751698c8d2d08c66c705684823d6ca3bb3c547c2cded291296f5777f4f0bea,1afeddf6051dcc2e95c5f545c39230db34991d7bbbdf2afd6a4cebd68c901ea3,1e77182b47558663ed58018daa402706d32cca00117ee505861f5ce5ef6bb498,01b1e23f73849126d71d4b9fb35f1b8a4c70f1134a72bfa54accd6bb235a0ada,2de87718a7a06f43588a28aeae265f463a406ec691b130df0be0b8edd40529ff,2a9ee62de51e659d8638a9b04a53995156a638ebe5b9527d0ea670aba21a3468,11fccd6917a44aa0ef6fca9ac867209f521e1d19a9033af6fd059b4626f3b15a,1acbe22d650932a736749e175f5cb606fc21000cafe22bbf4aaadc97d36ee1b0,18a18df8455085b2c63f368c81a806509936a1791670be7541e3c683eaef1e15,252856b73a5c4a04f1747732fea56b6db46414f5f6c87e06e68d3d385007a281,21e16309d9a3b334eed54abc690e0d741bc6a1b444dc7bc15cbf2968d3915a1e,28b0dccb2508aaf6ff11af18c2c18f29969c595799e6f1e8943e60273cb3243b,1e44015eee75766bcb41b165b29b7329b057356236b4e9c7d3c13fc0cfa4c170,10abc5e77bd61d8dc1bbe278488bf8280e9aace1a865c6a09fa6aa02b561b755,04653d225b30cef48a8343665edd00b20612ef058500f31b438a9c0de964f76a,0d1827a10b61ca657e4c6af5455fa0a78f79f0e65b0654dff135e2c2a328f889,26674fa611abe8f122cdf8d66c30bc38f008da4b47c518cade75598097cbc339,2748498e61bff4fe4c890df537e6b266da5d46a4a16baa349da371acb644321c,079eb16475c95b0666449c52138e4fe735aae1cd661c8705d8775d40b1685bd1,20eb1b4a9cf019275655c48d9c4c9989f0627fa3bfabeafa2c321316ce340426,22d03b74837185837c5ffaaf322db930a9611132eeebc518b0784feee939af4f,230d104ca3c0047e7461812c489e57d1020f4015646d0c0dc800418d37a717e4,1fb570d4ba3aaa6f10bee83a993091ecbae52a695cecc6c2a1c94f1cc3fcbe6a,1cb4a2e6fec39b0ce150336d6a985f4a2e0ec6a1e82de02cc5ca545a353eecb7,1b910db2bb9f16bb6be05eb00f70b19ff44ed969029eb6fb0824ee6f8744b7a3,0144edc2ad083966ed806e11d3b68b96f60f93af87e185e58ab6fb96745503a2,0ac9fc5f3ec4f04c42043bfba664a51828b321528d333417f1f2b832cfe9ca49,066b885c3aee67c4942dc788e7b594472ae03fdaec46d72eb4a88fb374e0bdc9,14db53096e3886025efd5e6ab776ad84ab4a2ea1eecedcda07b473a512ee4407,0aeb01c642b3842fff949efd96d06f64a76ef7decb0515ffe84b97a317fe3c02,010af446fa6f971c83c0f524f570bb4251eb243d2c653174a0e5c11f7c3bf4b4,1e4a12c601cac5fe4e8ca4204b7f2c7dc41210acb8bae127da9babca4c7574f0,15a19deb4d91b4e7b0b6569e77b525f42abc8310e976286eb1128167f3359008,0a3eb3426915aac049666e12a2def83da746cd83b5d6e9969ad46c48b0021bf9,17ee41f86670b3238e67b3935788995596f380845a26e9cd0199c3b6ab8d9d34,129a8a1b69adec6c29a7252a3339c98f3447a7c248b59bdbd86bfa26ac361d5a,0f3e50b72a65719a98991bfdcc84ddbfbbd105b4e845a1343a030dca3890baba,21d6f2e545502ff21579037d2e1a279b1219a14d8b0a12fd0130a91d4f9161c9,22ffb1ba3c7267d553097f1b3b8d2deb457ea070e2c4f390d8d013391ae118d8,0d40f9d45666319abd007befe5a4d1c565fbcaf6733abc43fea4ca96ebdd3381,1e36929b662f1a8d6764fddaece7474bba8bfde4511a8dbfb684610ed3cd8f68,26779c69473e8ae0ec577045743732caf7b182e7e6d23645891afabeae792b07,0cf9d3d66f24e1f5ee954c70bac2e1d8da99f300822c047bb5bb4b5c54ea88b0,0401fc4fe802bd7f3611f693ddd0c1d1d7f45d4c70ea4cd132606467c6598752,2dd26d37a6f369d1ec2a24c4362ae78aea0981239f77c377373d58341bbc5f48 +sub|64|08e18f65b91169caa761bd3216d7ac010352a041cf83d94441cd01efb7efc041,05c905a68b638bd30bb06ac0cd844982dac3c337d0d6479c38595a4641491f2c,067b4fc60bed16a909bca1ef3dc04a48880f602bb016a7985740d3d275bbaa06,13e77369d11526df2e0dc9aa0b1468ee5b7ce35e038eb8b02d6aad3e06bfb47d,1fe77bfd637f9f09114cc41f40fecfda68be042ec6ea9a6a59f9846a16ffec06,23827edbc1cfc456261458dbbfc37a108c637f5e60eaf69064a4af6c393048d1,178d19a786f3b4f1b9df772effaecfece39aab6ddb687845b8d4d46468b97e5e,2bf335a1dba54a4fb5b02259c9a06c11a9bb014b477df2e1955290f9dec24117,2186d955510817bcceabe943924ea3a8995094094ad0a5052e12e6a81650c0bf,07eae1dd3beb9921bdb8613737b2ad2050906ddfd6131c4e06604d1c28dbaa4b,112b8c907027e45bb61367766c4ef5a80022210ae76637040564890ff60bd6cd,19eb2f05eaede9bae5a9331a99a8b621d937777a6168201fd6d192cd7e0fb3d6,1a359e5f42a0a18bfb09d2a7642a645cc7e617843c515d249ff16c133da392b9,22d922871eef34cf8ebc9f31133b2c44b074c8e5195abe7242c7655b076946a8,00a06d60714522392477280015af9e5ec51d70125d06e25b089d96b1a349f9d0,18e6002e1649da58cee1fc73234387de1e9acad0972cca1994122eb9967a4bc5,0a5a3ceff59c63b1b14a2d4ca6c3ec1ae3f56d63e62228f10224f375d46edc66,05f6f2f0ac035357c5df93986f55cafe6b63a198ce3583f29ccb5a8fb635a068,2991b41a2f8054938301c8a3568a48fe849715f041997b99be6572ebf98d8152,06f3fff25f0cc2efdfce6624933577ae2f444d013252ac18a18e022c6a080989,12ac5dd14b7ec7699132723d51f0918d21cfba315b8745b22f15db3f36e59c31,008fec4dc13338f27a2c4a1cd803d3eae840592419e244db3e6b815c7eecd3cb,019a227716914afd7713f2a408f9166c100ab5bb5a5f2edf3a4e2a5f06278d4e,00219941f5eae408312a1ee9ceedd637566e48bf6d59c90abd42983a76f236ce,0f9725d62b9605989ce30cf476da05b62eae08e8193b41d73afeecf864d2364a,066031bb4be3018dfc9b7ff36911172a63b420730faeca4ac3ffd49234ea0a98,2c019561d60c12ac62f1f41d0fd4da04984035fd09a60e69d4e7b441ba70f3a0,266ec1cf74f68b8ebaeab4881f6f991e142ce113b31dc5da037106a9a0d8ac8c,0d732a8f831451d78f0df110316cf1d7e99105450af5d0abf43d900d92961fec,119446de68dcbc090614b86c58531ddac817e9a6e62283210f18e895ff0f4835,0d1b55e07ec1c1a6eba0735b71d54966a15a6be39705ee69ef4a83e5cb49db8c,20be6a4b85ed80fe52922fbb5c1d090c854738cdd5c84b948a024af0757d2df5,1d7daac1c230c9bac495a3a5e345055f962d75e3dd1d7a33bfd849888c61c5c5,2d718356213fb130a67de0d0bdbd78342d9347782e1445ad59a6eb124ff6e850,041dc2fe70b8b41b0a017676673da8901d26ef65243f72a6331a55074c3438bc,0483ff24387e8d31f65cc73701625647b2517b64d977cefc1a948ca5e4be45a6,3039410c7af0fc2dda75761337f477653b575c914b67a517b6c0f255ef0456c7,1654173bec613e3cd4127af606926d4c7c1964a70c229a138548f2a2854a614e,2486eb178da4c9f928e033175b113acf6e339d8d94cb1a2a9f4c04009a0b9dfe,28991a3a92d69c54d206eb141fbd070259ea34dfa5accdb70f2527cfd69cdfc7,06ad429b49b8d169488d0ac8721686af12aa965b94a3d27f75b4317c7a952435,1dce12c81fdc1decd362f2125a1eba528a46586a0f012040a9871258a27f7531,1cecdddd43af38a2085e23d027945dd3088538b22db13122b0e13b11d7795395,21d91dc400c75749a16334b0d5a80e45f9eb2184ff9d5e9f95dfe0a306186bd4,263f585b355b64d9d4f2823a7bbfbdb7716c1b727a838e40e73d2ed96d70b2a3,18e31747e101b21e899762c7e72535792b36c054baa43ebe386c0d7f90e58387,2dfaab0e42ac3d29d272a9daa4d1ad749b3a5a3dc6487cf85d763dbcc2e41654,0af0949b2e5ffacb483977d61dec3007097f9b82963697e68f01653c1cb94052,01c44cf0fb267eb34a349e93fd9008a169264b3641361766dde8d6c98152980c,28d57fb38272af71418968464958aee0eff79e083cd207d6fb12f148dae5110e,2f70b3c9a91dd08035404195147364cf71e0b3f903e75dcdd9aa742da165b895,2a8bb75d4a4d64a766bc67a58fdcabee4c67ebf55a0bf21041347223f070bb06,188fe5bd5e738c589c8c1d2ba38551f05ea906271b3728c77a4f2ebba8202dc4,1721a1047915e206909a53d2ed98d929759ab0d19091cb8c6a9714f586152801,2e5a5b14a67df128e31943b6f5ad3459292c3dd3eaef678129c821182e526051,1846ffa436ebb77a03d3e48f72e17eb85117d704894bed8b385c002f6308d2d2,098f359747675d562bd2b7344e399bea020643d9f1fe81516d629ad27905e9b9,0ce18a33862d3071c046c9882785da061b539d2b04f4b2535f71c4d8948fc624,1e3e714b236edf292764c86d3b6cd6395189cb42d771757bc9139655ab45d1da,04660d1d9270afbf3a34024958138513f8ffc7dc5eb012de8cd364ad3e5d20f2,1af2b683b29a7dfea6056588d3087741cf927ac9995d8e2050c0f15707e6363c,1509bbc931ddbcc9eb15ef333d9e0735be03305894b87f32a4cb77149bbd2601,2b3dccc00eed8319a9091f75a8090fefcc7059f6c698894951f664cdc53a03e5,03572b106830187278c625444959981edbec3ec42fcddf1ae1f97f0fc450fcb9|2bc6453a148b9b40181c97049a146619baea7ada57695078fb93734427488fa1,2180636d5efbf81b5de925e450dd408c931b078450d75c752b28c5fa4d64b478,229603124923b4de550c627c038797f248b66bfab09c8952b0005132f00b9be5,2c26a6e64443087404b76ae5243b85e40ca799412abf203fcac90e490ee0f2fa,218e4c52cd26e410b62468c7eabeccbb4a62e01164d167d34b775f60aecc3031,1e45f1422144347185569c95d167caf3c849401de2c7e184ae7a0b5401643bc8,0ec9f02eebacea2517e2ff246796375caa11d2290a1f986c2f0a811d319ec425,0cafc60e89694bffb3248ddf683cea8b9c30c17bc9dfdc04e436729ec70166db,0c8beea842d2b0f6316e7631e010944037fec239d8ec635dd22cd09a12181669,2849584679df81d3f4546e17fc960ce8d058267fcf9318a5d40b3a2e0e53bdc3,28d99f5b94409dc0e204796fa8c0675d84c8e79553fdd218bcb1cfae97782b2b,28725ed3abf094f81e6c2f2a0cedaf3782841e3b5f12ed9b0a3a20c2d373c114,2122c3dea1a171fbe0c314a191278d7d79879388780dc69e4f5bfa156bfb7d1f,01b609ff3be168fd6d9d6428f07f3af6341f5d2f8a28b5eb508b6d00595abdb2,1ecdbe667fae1a800079887ab996a22a3b047d7fb9f410051cbf6a2de51fb357,0f33a882d555f9b2d624466b9658930863d11ad2a63accaa2e9ad7c184e74fbb,02a4a4f011936088c435916341cfdb09107fbd5150778b4258402be0969ae2c2,24b532d4a55f95c9d79d5cb1411aca4a04e4eb36d5f076f3483c3ad5036c0309,0bc52a9227a8eaf64f876eaffbcc156342f523534bac76f9a3593251200357f9,288116a669c60d9c86f89f43b4ee5f1c0c6f7846907b41107108f34b15470261,08528024b99f04c50493830871a19f4e12c9634a6057e54b3b37109755aa8272,1de72bdd86224d71732bb770d23c531beaec70dbf79ca02a47b3db89707ee0cd,0017bfc85cf34629600958fbaa66051e3c663b57f01390c6107eac5c1d327d8c,2dc6ddd6b1b58b3b276009c4df38890ee3d22607245767d44e9e20b35d12f331,1b07c057b9886004e9559cbbd379939b27f83003cc7e10a5d3a783b33d47fe1e,0b9c9badcbc14912f2d44aa75f560974ee69fca6995470ac3905c6b3f209a6c2,1f2e9b3e702ec0248bd2efb0d109345f8c14b2581ff58de6b9a51dea08fdee11,22971a9bb18b9a4dc3a4c7bae3b9c58fad3da8addd0c692c8254b56e3a16718a,17b52c27b747f82d62668622cd387995cad30fb0ebd2ad5af24fad2abd718295,104d1c2b70c6f72be8c0925010baef9953aeb80d5eb9f8a04da640d2d48211e9,1b9586eaa646e95013713bbd50ec45c2f541ed7402e1037ea4f3dc41716948af,2de9e58649b9959730ffc760d7ffc27a5343e4dcdaa60ec48da0ea644a27937c,239269989ad6f3fcb5768488e6c84b25a0a11f464501bcfe23b0560e18fff191,0758083f1b22bded9c55a84c22a0e0db00b38fd5d0a61dff2dc5a68f896e0f1b,08fa64a29aa9164a744af47ede21f8177253018136c6e239be1b8dbb56f4bfcd,21e35081d92d5bbf2c71319f6ace65f13db75ee66e4d49cec3e0ccdab30d7d93,277356f4c80098fa2a63dd988173935ec739d25bcfbd75ae2ac474eab73fdb56,21aee89b6a99bcf34a8267128e7d3af7e1c5656ed3b35d83971060321c1dfa84,2cc87ea5f07cef57e5c5d72cc2bcb717aa62ca5ea49a4160d0c804aa24286629,2a9b6facd1cc895862a9555193f20a8b77aac49bc2f867f2e5351db3029ccf89,1c5fcdb15a0733152bd47663d687d121ef64a9b9cfc9398e524c1010bd11f3af,01e75e0c9a5e8c823d5bf6283f11d79a309ed1ff4deba681f8423cc4217d4939,302c137c9c46029491425553c48559d44dbd763834361f9b58cb0edc4dc59923,2a1c3e619c095f9b82cd6fb5bb49fbb72297a02c7cbac8ecb6270360712c4bd0,0b69e3da58de74b6d0de318dd978263cacd7608587176835e75bc250f6e45100,224b338a3ef4de5770bd1eea40c0c7fc25b049464c4865eafd68a0472f0446c3,08d52bc0d973cac47a0b6364c4653f2fb7d9cde59fb7cac79b14478aa1fca776,09eabe6e3fd88b3716c3e694998a7d7da1ca931f589844f378b30e68f63503b5,0926b4d5478d057cb5600069994066c33e48aca889cefe990a62c0d996aba3f6,0899c306592e87d4fa87d2952d9964be8a276e7d694c9a2ee9b4c56a9156e3a7,1f3dad6f39de95a7d19ca841b88d200b7a6544fc2e8cf3eb44d32d309b0fbc5c,1b7a3500e475f06a024a34af6959d26306887f640923a6efb3c004d7f2c4d503,22131bf7ebd3be91652a969d80dafeaa70d1afa51459316064673320f7e1ee36,00cca0f3ed5ad11cfdcd5fc069efc02c2158cfb2c9951e409702aec125787533,14a47d79a4619b6cfede2729bf0ded93334f5236d77fa4ebf285cea26de3bd0a,275b9f85d4ab5a4a4d157d24db24b76492ed16f8d8b3243a4589032ec587e7e9,1847bd4dfde8d29be9a64c48dfe08bb110135d73990b91ab93ce0e4ad68b7810,161e2786b645376392c2b593140753e52a2b0345ddd0413d795e4e60865152b4,1f66d6fc1428f29b4debf9392bb953e93ca5e7fc1582b759797329d5309761a8,19d0857dd3be6ace2d30fb9194d3c237c18c3607f26a7ae129b0fc6195706e76,0b84e5e594a40ce246520abca12ebb89281f081e4d74a825385a0967a692f4cb,285466801e78c555bbcfa2f3fea6330044caaaf0672cf5da54d1c9dba92d62b0,09287e02ba46da8f45591cd4b7490a3f33b7eb9e240b3419244bf52df11f836e,2a7b42273ec3515f7363ff7fecd14f6c0e1d425f6fa9e45c5543d924576b628f|0d7f989e85b76eb447956be3fe449e44709c0daff1d3f95c8a1b843f80a730a1,14acf0ac0d9933e166178a92fe2861536fdca3fbf9b85bb8511289dfe3e46ab5,14499b26a3fb01f46d008529bbba0ab3678cdc7979338ed6eb22783375b00e22,18251af66e03be94e1a6a47b685a3b677709326552890901a6839488e7dec184,2ebd7e1d778a5b221378a10dd7c15b7c468f0c65dbd2a32852641a9d5833bbd6,053c8d99a08b8fe4a0bdbc45ee5baf1cc41a3f407e23150bb62aa41837cc0d09,08c329789b46cacca1fc780a981898903988d944d148dfd989ca5347371aba39,1f436f93523bfe50028b947a616381860d8a3fcf7d9e16dcb11c1e5b17c0da3c,14faeaad0e3566c69d3d7311b23e0f686151d1cf71e441a75be6160e0438aa56,1005d809a33db77781b438d5bc9df894a86c2fa880397439763708820a87ec89,18b63ba7bd18e6c48c5f33bd450fe6a7a38d21be0d21d57c8c94aef54e93aba3,21dd1ea5202ef4ec7f8d49a70e3c5f477ee741877c0ea3161079679e9a9bf2c3,297728f38230cfb9d29703bc54842f3c76926c443dfd071794776791c1a8159b,21231887e30dcbd2211f3b0822bbf14e7c556bb58f320886f23bf85aae0e88f6,1236fd6cd2c8a7e2dc4de53bdd9a5491b24cdadb1ccc42e72fc02217ae2a467a,09b257ab40f3e0a5f8bdb6078ceaf4d5bac9affdf0f1fd6f657756f81192fc0a,07b597ffe4090328ed149be964f41111d375b01295aa9daea9e4c7953dd3f9a4,11a60e8ee7d55db7a6927c9dafbc59118eb29eaa71fe7d909871154ea2c99d60,1dcc898807d7699d337a59f35abe339b41a1f29cf5ed04a01b0c409ad98a2959,0ed737bed678557d11260c975fc870ef4b08bd031b90db997467047544c10729,0a59ddac91dfc2a48c9eef34e04ef23f0f0656e6fb2f6066f3decaa7e13b19bf,130d0ee31c428baabf50d8628748d92c2587d0909bff15423a999b66fe6df2ff,018262aeb99e04d4170a99a85e93114dd3a47a636a4b9e1929cf7e02e8f50fc2,02bf09de2566f8f6c21a5adb7136a5859ad00b00c2bbd1c7b2866d1b09df439e,24f3b3f1533f45bd6bddb5ef24e1ca782ee9c12cc676a1c2ab395ed9178a382d,2b27e480615358a4c2177b028b3c66129d7e0c14f013ca2fcedc037232e063d7,0cd2fa2365dd5287d71f046c3ecba5a50c2b83a4e9b080831b429657b173058f,03d7a733c36af140f745eccd3bb5d38e66ef3865d6115cad811c513b66c23b02,26224cdaacfdf9d3e4f7b0a3e5b5d09f46f1dddc98dc93e245cfd876c5249d58,01472ab2f815c4dd1d54261c47982e417469319987688a80c172a7c32a8d364c,21ea1d68b9ac7880907f7d54a26a5c00d44c66b80dde5b7c8e389d3849e092de,2338d3381d658b90d9e2ae11059e9eef5a373c3974dbad61404356201b559a7a,2a4f8f9c088b75e7c76f64d37dfe12971dc03ee611d52dc6e009e90e6361d435,26197b17061cf3430a2838849b1c97592cdfb7a25d6e27ae2be14482c688d935,2b87acceb7413dfa4e06c7ae0a9d08d5d307d62c673200fdb8e0bcdfe53f78f0,1304fd154082d19c823bdb4e181548b39cce04c6e4e3f5be9a95b55f21b0c814,08c5ea17b2f06333b011987ab680e406741d8a357baa2f698bfc7d6b37c47b71,25097d1362f9217341e05999f9968ab1c287e780b228ad21321a8804592c66cb,2822bae47e597acafb6aa1a119d5dc14ec04bb7769ea495b1265f4ea65e337d6,2e61f900a23bb32627addb790d4c54d40a73588c5c6dd6556dd1ffb0c400103f,1ab1c35cd0e33e7dd508da1b1d100dea4b79d4ea3e940982674a16ffad833087,1be6b4bb857d916a9606fbea1b0ce2b859a7866ac11579beb144d59481022bf8,1d2518d3889ad6372f6c1432e4905c5be2fbaac2733482189bf821c979b3ba73,28212dd545ef97d7d6e60ab19bdf6aebff8769a0fc9c0644239ad2d684ec2005,1ad57480dc7cf023041450aca247977ac494baecf36c260affe16c88768c61a3,26fc3230833e73f0d12a899427e5c5da2dba5f56e81549647ee562cc51e13cc5,25257f4d6938726558674675e06c6e44e3608c582690b230c261f63220e76ede,0105d62cee876f94317591418461b28967b508633d9e52f3164e56d326843c9d,2901e68e94cb19604d24e3e0e5d0fa3b531186d63120895f17680b83daa6f417,203bbcad2944279c470195b11bbf4a2265d02f8ad3856da8115e2bde498e2d67,1033065a6f3f3ad863a399535be644c3f77b6efcd55a69e294d746fd0655fc39,0f11825c65d7743d647232f62682d98b45df6c9150e84b208d746d4bfdabe603,26e1183853d16df0efb1cc44a42baba3160b3eca809767f859c9f12ea03e3f8f,165500108bbb10e992ccf41283a918fd5441e11ec6fcad4bd3946634609cb2ce,19b5dd9b021c55bbe43b1c8d369f46c5f5dceb9d136fc29537425275c06ea347,214fae914371fd596f0ead21193e1fb0e65ea8542a5239e236b4f2948d80eaea,21abc6bc2ab02ae3fa7cb0a1efda68961a26ceaed2ac60371d76821b927a71aa,2727b11fb1199937e5d459ab94ffde7e195c822da0dde1a729f56c0bfe3e7371,2f3be8c1f0778cb791c914ea9134daad3d17cb8f3ba82eb3938262146aae7033,1af9d6129fe3e51ac5534c6e44c11b395fa77a1ce5ff088ea7045ddf98ecb27d,0f6dd09e1df6711c5fb35acc31d9bbb8a77372ab4be8e5fb1866e7ef61534171,1d19a3bbf496979de79691f5c0792c92a16c6db0a744f9e993dba2cce28fc352,22154ebd54a6a88a63b002a0f0c005b098b86e58a28d55302daa6f9fd41a8077,0940375c0a9e673cbdb26b7ade09a10ff602e4ad39dd6b4fd0979b7f5ce59a2b +neg|64|08e18f65b91169caa761bd3216d7ac010352a041cf83d94441cd01efb7efc041,05c905a68b638bd30bb06ac0cd844982dac3c337d0d6479c38595a4641491f2c,067b4fc60bed16a909bca1ef3dc04a48880f602bb016a7985740d3d275bbaa06,13e77369d11526df2e0dc9aa0b1468ee5b7ce35e038eb8b02d6aad3e06bfb47d,1fe77bfd637f9f09114cc41f40fecfda68be042ec6ea9a6a59f9846a16ffec06,23827edbc1cfc456261458dbbfc37a108c637f5e60eaf69064a4af6c393048d1,178d19a786f3b4f1b9df772effaecfece39aab6ddb687845b8d4d46468b97e5e,2bf335a1dba54a4fb5b02259c9a06c11a9bb014b477df2e1955290f9dec24117,2186d955510817bcceabe943924ea3a8995094094ad0a5052e12e6a81650c0bf,07eae1dd3beb9921bdb8613737b2ad2050906ddfd6131c4e06604d1c28dbaa4b,112b8c907027e45bb61367766c4ef5a80022210ae76637040564890ff60bd6cd,19eb2f05eaede9bae5a9331a99a8b621d937777a6168201fd6d192cd7e0fb3d6,1a359e5f42a0a18bfb09d2a7642a645cc7e617843c515d249ff16c133da392b9,22d922871eef34cf8ebc9f31133b2c44b074c8e5195abe7242c7655b076946a8,00a06d60714522392477280015af9e5ec51d70125d06e25b089d96b1a349f9d0,18e6002e1649da58cee1fc73234387de1e9acad0972cca1994122eb9967a4bc5,0a5a3ceff59c63b1b14a2d4ca6c3ec1ae3f56d63e62228f10224f375d46edc66,05f6f2f0ac035357c5df93986f55cafe6b63a198ce3583f29ccb5a8fb635a068,2991b41a2f8054938301c8a3568a48fe849715f041997b99be6572ebf98d8152,06f3fff25f0cc2efdfce6624933577ae2f444d013252ac18a18e022c6a080989,12ac5dd14b7ec7699132723d51f0918d21cfba315b8745b22f15db3f36e59c31,008fec4dc13338f27a2c4a1cd803d3eae840592419e244db3e6b815c7eecd3cb,019a227716914afd7713f2a408f9166c100ab5bb5a5f2edf3a4e2a5f06278d4e,00219941f5eae408312a1ee9ceedd637566e48bf6d59c90abd42983a76f236ce,0f9725d62b9605989ce30cf476da05b62eae08e8193b41d73afeecf864d2364a,066031bb4be3018dfc9b7ff36911172a63b420730faeca4ac3ffd49234ea0a98,2c019561d60c12ac62f1f41d0fd4da04984035fd09a60e69d4e7b441ba70f3a0,266ec1cf74f68b8ebaeab4881f6f991e142ce113b31dc5da037106a9a0d8ac8c,0d732a8f831451d78f0df110316cf1d7e99105450af5d0abf43d900d92961fec,119446de68dcbc090614b86c58531ddac817e9a6e62283210f18e895ff0f4835,0d1b55e07ec1c1a6eba0735b71d54966a15a6be39705ee69ef4a83e5cb49db8c,20be6a4b85ed80fe52922fbb5c1d090c854738cdd5c84b948a024af0757d2df5,1d7daac1c230c9bac495a3a5e345055f962d75e3dd1d7a33bfd849888c61c5c5,2d718356213fb130a67de0d0bdbd78342d9347782e1445ad59a6eb124ff6e850,041dc2fe70b8b41b0a017676673da8901d26ef65243f72a6331a55074c3438bc,0483ff24387e8d31f65cc73701625647b2517b64d977cefc1a948ca5e4be45a6,3039410c7af0fc2dda75761337f477653b575c914b67a517b6c0f255ef0456c7,1654173bec613e3cd4127af606926d4c7c1964a70c229a138548f2a2854a614e,2486eb178da4c9f928e033175b113acf6e339d8d94cb1a2a9f4c04009a0b9dfe,28991a3a92d69c54d206eb141fbd070259ea34dfa5accdb70f2527cfd69cdfc7,06ad429b49b8d169488d0ac8721686af12aa965b94a3d27f75b4317c7a952435,1dce12c81fdc1decd362f2125a1eba528a46586a0f012040a9871258a27f7531,1cecdddd43af38a2085e23d027945dd3088538b22db13122b0e13b11d7795395,21d91dc400c75749a16334b0d5a80e45f9eb2184ff9d5e9f95dfe0a306186bd4,263f585b355b64d9d4f2823a7bbfbdb7716c1b727a838e40e73d2ed96d70b2a3,18e31747e101b21e899762c7e72535792b36c054baa43ebe386c0d7f90e58387,2dfaab0e42ac3d29d272a9daa4d1ad749b3a5a3dc6487cf85d763dbcc2e41654,0af0949b2e5ffacb483977d61dec3007097f9b82963697e68f01653c1cb94052,01c44cf0fb267eb34a349e93fd9008a169264b3641361766dde8d6c98152980c,28d57fb38272af71418968464958aee0eff79e083cd207d6fb12f148dae5110e,2f70b3c9a91dd08035404195147364cf71e0b3f903e75dcdd9aa742da165b895,2a8bb75d4a4d64a766bc67a58fdcabee4c67ebf55a0bf21041347223f070bb06,188fe5bd5e738c589c8c1d2ba38551f05ea906271b3728c77a4f2ebba8202dc4,1721a1047915e206909a53d2ed98d929759ab0d19091cb8c6a9714f586152801,2e5a5b14a67df128e31943b6f5ad3459292c3dd3eaef678129c821182e526051,1846ffa436ebb77a03d3e48f72e17eb85117d704894bed8b385c002f6308d2d2,098f359747675d562bd2b7344e399bea020643d9f1fe81516d629ad27905e9b9,0ce18a33862d3071c046c9882785da061b539d2b04f4b2535f71c4d8948fc624,1e3e714b236edf292764c86d3b6cd6395189cb42d771757bc9139655ab45d1da,04660d1d9270afbf3a34024958138513f8ffc7dc5eb012de8cd364ad3e5d20f2,1af2b683b29a7dfea6056588d3087741cf927ac9995d8e2050c0f15707e6363c,1509bbc931ddbcc9eb15ef333d9e0735be03305894b87f32a4cb77149bbd2601,2b3dccc00eed8319a9091f75a8090fefcc7059f6c698894951f664cdc53a03e5,03572b106830187278c625444959981edbec3ec42fcddf1ae1f97f0fc450fcb9|-|2782bf0d2820365f10ee88846aa9ac5c24e14806aa35974d0214f3a438103fc0,2a9b48cc55ce1456ac9fdaf5b3fd0eda4d702510a8e328f50b889b4daeb6e0d5,29e8feacd5448980ae93a3c743c10e14a024881cc9a2c8f8eca121c17a4455fb,1c7cdb09101c794a8a427c0c766cef6eccb704ea762ab7e116774855e9404b84,107cd2757db20120a703819740828882bf75e419b2ced626e9e87129d90013fb,0ce1cf971f61dbd3923becdac1bdde4c9bd068ea18ce7a00df3d4627b6cfb730,18d734cb5a3deb37fe70ce8781d2887044993cda9e50f84b8b0d212f874681a3,047118d1058c55da02a0235cb7e0ec4b7e78e6fd323b7dafae8f649a113dbeea,0edd751d9029886ce9a45c72ef32b4b48ee3543f2ee8cb8c15cf0eebd9af3f42,28796c95a5460707fa97e47f49ceab3cd7a37a68a3a654433d81a877c72455b6,1f38c1e27109bbce023cde40153262b52811c73d9253398d3e7d6c83f9f42934,16791f6cf643b66ed2a7129be7d8a23b4efc70ce185150716d1062c671f04c2b,162eb0139e90fe9dbd46730f1d56f400604dd0c43d68136ca3f08980b25c6d48,0d8b2bebc2426b5a2993a6856e462c1877bf1f63605eb21f011a9038e896b959,2fc3e1126fec7df093d91db66bd1b9fe631678361cb28e363b445ee24cb60631,177e4e44cae7c5d0e96e49435e3dd07f09991d77e28ca677afcfc6da5985b43c,260a1182eb953c7807061869dabd6c42443e7ae4939747a041bd021e1b91239b,2a6d5b82352e4cd1f270b21e122b8d5ebcd046afab83ec9ea7169b0439ca5f99,06d29a58b1b14b96354e7d132af70f5ea39cd258381ff4f7857c82a7f6727eaf,29704e808224dd39d881df91ee4be0aef8ef9b474766c478a253f36785f7f678,1db7f0a195b2d8c0271dd3792f90c6d006642e171e322adf14cc1a54b91a63d0,2fd462251ffe67373e23fb99a97d84723ff38f245fd72bb60576743771132c36,2eca2bfbcaa0552c413c5312788841f11829328d1f5a41b20993cb34e9d872b3,3042b530eb46bc21872626ccb2938225d1c59f890c5fa786869f5d59790dc933,20cd289cb59b9a911b6d38c20aa752a6f985df60607e2eba08e3089b8b2dc9b7,2a041cb7954e9e9bbbb4c5c318704132c47fc7d56a0aa6467fe22101bb15f569,0462b9110b258d7d555e519971ac7e588ff3b24b701362276efa4152358f0c61,09f58ca36c3b149afd65912e6211bf3f14070734c69baab74070eeea4f275375,22f123e35e1d4e52294254a6501466853ea2e3036ec39fe54fa465865d69e015,1ed007947854e420b23b8d4a292e3a82601bfea19396ed7034c90cfdf0f0b7cc,2348f892626fde82ccafd25b0fac0ef686d97c64e2b38227549771ae24b62475,0fa5e4275b441f2b65be15fb25644f50a2ecaf7aa3f124fcb9dfaaa37a82d20c,12e6a3b11f00d66ef3baa2109e3c52fd920672649c9bf65d8409ac0b639e3a3c,02f2cb1cbff1eef911d264e5c3c3e028faa0a0d04ba52ae3ea3b0a81a00917b1,2c468b747078ec0eae4ecf401a43afcd0b0cf8e35579fdeb10c7a08ca3cbc745,2be04f4ea8b312f7c1f37e7f801f021575e26ce3a041a195294d68ee0b41ba5b,002b0d666640a3fbdddacfa3498ce0f7ecdc8bb72e51cb798d21033e00fba93a,1a103736f4d061ece43dcac07aeeeb10ac1a83a16d96d67dbe9902f16ab59eb3,0bdd635b538cd6308f70129f26701d8dba004abae4ee5666a495f19355f46203,07cb34384e5b03d4e6495aa261c4515ace49b368d40ca2da34bccdc41963203a,29b70bd79778cec06fc33aee0f6ad1ae158951ece5159e11ce2dc417756adbcc,12963baac155823ce4ed53a427629e0a9ded8fde6ab850509a5ae33b4d808ad0,137770959d826787aff221e659ecfa8a1faeaf964c083f6e9300ba821886ac6c,0e8b30aee06a48e016ed1105abd94a172e48c6c37a1c11f1ae0214f0e9e7942d,0a24f617abd63b4fe35dc37c05c19aa5b6c7ccd5ff35e2505ca4c6ba828f4d5e,1781372b002fee0b2eb8e2ee9a5c22e3fcfd27f3bf1531d30b75e8145f1a7c7a,0269a3649e8562ffe5dd9bdbdcafaae88cf98e0ab370f398e66bb7d72d1be9ad,2573b9d7b2d1a55e7016cde0639528561eb44cc5e382d8aab4e09057d346bfaf,2ea00181e60b21766e1ba72283f14fbbbf0d9d123883592a65f91eca6ead67f5,078ecebf5ebef0b876c6dd703828a97c383c4a403ce768ba48cf044b151aeef3,00f39aa93813cfa9831004216d0df38db653344f75d212c36a3781664e9a476c,05d8971596e43b825193de10f1a4ac6edbcbfc531fad7e8102ad836fff8f44fb,17d468b582be13d11bc4288addfc066cc98ae2215e8247c9c992c6d847dfd23d,1942ad6e681bbe2327b5f1e393e87f33b2993776e927a504d94ae09e69ead800,0209f35e3ab3af00d53701ff8bd42403ff07aa748eca09101a19d47bc1ad9fb0,181d4eceaa45e8afb47c61270e9fd9a4d71c1143f06d83060b85f5648cf72d2f,26d518db99ca42d38c7d8e823347bc73262da46e87baef3fd67f5ac176fa1648,2382c43f5b046fb7f8097c2e59fb7e570ce04b1d74c4be3de47030bb5b7039dd,1225dd27bdc2c10090eb7d4946148223d6aa1d05a247fb157ace5f3e44ba2e27,2bfe41554ec0f06a7e1c436d296dd3492f34206c1b095db2b70e90e6b1a2df0f,157197ef2e97222b124ae02dae78e11b58a16d7ee05be270f321043ce819c9c5,1b5a92a9af53e35fcd3a568343e351276a30b7efe500f15e9f167e7f5442da00,052681b2d2441d100f472640d978486d5bc38e51b320e747f1eb90c62ac5fc1c,2d0d2362790187b73f8a20723827c03e4c47a98449eb917661e876842baf0348 diff --git a/testdata/zolt-arith-diff/msm/g1_fr_cases.txt b/testdata/zolt-arith-diff/msm/g1_fr_cases.txt new file mode 100644 index 00000000..e15656a4 --- /dev/null +++ b/testdata/zolt-arith-diff/msm/g1_fr_cases.txt @@ -0,0 +1,6 @@ +# name|scalars_be_hex_csv|expected_infinity|expected_x_le_hex|expected_y_le_hex +case_0|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49|0|f8faa90efa8b0bd3bf39e925d4b88082f442e1358fa658266876599aaae34406|8542f190037b02c6442904936b82ec3f50b520e88e599203f6262606f593de11 +case_1|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233|0|00920062e578e8a5b30042336de2380347bdbf67063d697d5ba5d172b5e59b0e|31bc9899678abf7f2feeb85b11f869a25e37d382e56f6e117ec524dbbf81b325 +case_2|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681|0|b2e690229aa4b5e13ac358cb35bbda40125b5f56f71313cccdeece9e1bfe6527|a3ec5d2452cdff746b41ea928064329f25a12cb51add3d353b4d769278e70416 +case_3|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad|0|35967244de17a52c4248fbf988f7c5fbaba5e576551901c8718ba6a1d911462e|16c46535da0116deb84e866595268729d6da4b3cee9578965f9efd753f68c325 +case_4|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad,14a266f67248f8b0281624f8624aee9e2e46f36a706def54e190a8324d0aa9dd,0b6f98682de3c12e476e4c08ecadc16898af8f98e42045c6cab0b33b9566d3c0,1080d264fe1d7715509c1583f080f903b0d9df6d29963f9f6b20a5cd8cc65199,24366f8705222747ce56c85b18282e2fd68a6e4f2a50087c471b556068d10b5f,29cbc5b641430a2036672aa0626bca66e71e6aad7ba6758abf4271df73635fdc,30001a7578c0efcc23a4de0a082794619dcfaa78071d54e0cdc684c4c916562c,14914203a175a4b0ade0119b23188c58a97f8dc4dfe2414e86519d812ede0303,024ed1dca7f18ca67a9370f93db66c08117503d86426ad3fc973d61d6d3bd49d|0|1f46c30d493f0959586d5784749b1d9615787d4c8beaadfb72450238d742200a|964a37230215edd30c9e5db1687f5961959cce3b6608dc7713c44bdbd8c09120 diff --git a/testdata/zolt-arith-diff/msm/g1_i128_cases.txt b/testdata/zolt-arith-diff/msm/g1_i128_cases.txt new file mode 100644 index 00000000..2b84634c --- /dev/null +++ b/testdata/zolt-arith-diff/msm/g1_i128_cases.txt @@ -0,0 +1,6 @@ +# name|scalars_i128_csv|expected_infinity|expected_x_le_hex|expected_y_le_hex +case_0|-3432184301748786869|0|247045f0c624cf65f0914807fec5342413a2a2034b390e88c378af370c937321|2f88c8f11911bf2e28a1a38365c8111582837c4026c168e7ef3c5fa1ff580114 +case_1|-3432184301748786869,5465917739609016864|0|e09c4d8a2a98d962cee2d24d38caadcf4a08484e131a7f72ae393fbc4b72e62e|b9372bf08cd6b8886de630ceea89eab428525b58599f46a3423b73c6b1b0101c +case_2|-3432184301748786869,5465917739609016864,-8931409406514358511,444449029250614354|0|fef2828173c83484eb2a9d175fabe0b233915d433e88cd426e0b1561a4dc1028|3580f98a96cd14a586637713d52717155891eaced7f4103b908044f528119900 +case_3|-3432184301748786869,5465917739609016864,-8931409406514358511,444449029250614354,-4641060796183802297,2636573779290473940,-4005544475886449555,2580567053586513702|0|7fb3daaeadfdde0be34516e8249fa4fca2b8ba51cf651ab1b0d87f31a01b1622|b1d337b01978420456b46eebf6af6eae951efbc9c788576e9e3be9827f6e3c23 +case_4|-3432184301748786869,5465917739609016864,-8931409406514358511,444449029250614354,-4641060796183802297,2636573779290473940,-4005544475886449555,2580567053586513702,-5956667692204160253,8867121980748338376,-2079764280125841527,989397817734758202,-383480243135816833,2252632985988655868,-4035287474603250587,1226907499484551310|0|fefe8795bb1a635bb7acd6727f8592dd24e5117e5374be5d04c359f640693a16|1c0bedb61bec6cd27d4a717b33091e01ec4e92a610ae76bfcaecf00548086701 diff --git a/testdata/zolt-arith-diff/msm/g2_fr_cases.txt b/testdata/zolt-arith-diff/msm/g2_fr_cases.txt new file mode 100644 index 00000000..47f63252 --- /dev/null +++ b/testdata/zolt-arith-diff/msm/g2_fr_cases.txt @@ -0,0 +1,7 @@ +# name|scalars_be_hex_csv|expected_infinity|expected_x_c0_le_hex|expected_x_c1_le_hex|expected_y_c0_le_hex|expected_y_c1_le_hex +case_0|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49|0|b1de020b72dd3e9010048b86f8faedfbbad05ceb67ce92da4aec485265873614|72468d8405cc3d50a48cddf595cc105b0bc87eefe39d9954ea5cf696ca398d1f|a5c7ff75686e1667498f341a054e64d354e52b9af5af0f700f541102f23cff04|f3f99c37cd2c749df8946b61796dfd282fe8a4fd3442840907205b7096c00226 +case_1|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233|0|20e1e5aabb0dad6233ace009e984b30076861b66ccd0d667eda84548e9d49e01|9ea25c2d08110d29b835d6b223516bcc263243600f3d74f0df42669826934205|d207f0ace85e198c94f2501d9359c341f8a417392b44227102be5cb480341f0e|f7df70fb9e26fb89da351a8631e20140b05af2869387e8256bf07a78bec0e41c +case_2|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681|0|e09f95cb89496d88e2803f475675110764a4555663ca26756ca1716435b25707|ed1fff4c701ececfdf36854158fb29d5bdecc4d990109383a7a1fcd1e5e5e712|7621252f156ca6c42853e995d9a21047ad00f9fe033633b95542be0516e37a21|54fc08da1ace23f287b6635f0525230c7acb93d16d3c26df3f1155a519aea112 +case_3|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad|0|f44d9be6ea3cde876795216f77d0b6235375d3c3e80b0a0a6b95f51b3ad03929|8f9f5db4a1e614c70117ebf24155362cf19a12a4180082fe57c7e4b062aeb62e|ff075835b61effbdb402a390c73dc401a226c5cfe6df75443015d2b06b5b162d|ef9beb63573c6e9b8c50d476b0d669b35896ccbec5c6b0301fc0e1373a3f5c0d +case_4|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad,14a266f67248f8b0281624f8624aee9e2e46f36a706def54e190a8324d0aa9dd,0b6f98682de3c12e476e4c08ecadc16898af8f98e42045c6cab0b33b9566d3c0,1080d264fe1d7715509c1583f080f903b0d9df6d29963f9f6b20a5cd8cc65199,24366f8705222747ce56c85b18282e2fd68a6e4f2a50087c471b556068d10b5f,29cbc5b641430a2036672aa0626bca66e71e6aad7ba6758abf4271df73635fdc,30001a7578c0efcc23a4de0a082794619dcfaa78071d54e0cdc684c4c916562c,14914203a175a4b0ade0119b23188c58a97f8dc4dfe2414e86519d812ede0303,024ed1dca7f18ca67a9370f93db66c08117503d86426ad3fc973d61d6d3bd49d|0|aaddb4bee20e54b46e024bc1362a3f06a7b5166069d6c74de64285fe5ab5942a|2d29f435697f30ccd1da5923471ad7638da8161a5a2d526b08031628a770ac1a|cf03ab3e35d3ff0de7bd3eb6727ab12df91b69acc74d3be5c5e4d3e91b8dc91e|6c8c6ed442099e3e6d8fc13d7966dc266c9faa7a477c15734274b7a59f36f52d +case_5|11c4441c2ed92de99c84dd7ed6ee373469e64770d26447401cdedf5564706b49,0085dd9f4ae1f4b25fe13451033822e4f9561d88b9cae68e7082d4f20b627233,23821dc4131f0b76013eca2ff95fe81d253b1ae1b01b204b8865ce5e040763e7,20e676101ddd26d032793a39cfef36be5eba3fd72219d2cb392b9448de257681,29c52bae84243a8d8d099cd54e3856612dcaf7c879aa324ccb16a7a4f9da14e7,0aee008b28477dfd6afebe39ef6d4997ec13437e8a1bd16a625e1d0a48003bb9,2cf8ba3135ab618310997c06bbcf12738583729dd8bb7152c40fcc7df5fb8ee5,0d6d30baff79267fe260870fd97408a238dc01d0e10324fb9edfe5b93cd33aad,14a266f67248f8b0281624f8624aee9e2e46f36a706def54e190a8324d0aa9dd,0b6f98682de3c12e476e4c08ecadc16898af8f98e42045c6cab0b33b9566d3c0,1080d264fe1d7715509c1583f080f903b0d9df6d29963f9f6b20a5cd8cc65199,24366f8705222747ce56c85b18282e2fd68a6e4f2a50087c471b556068d10b5f,29cbc5b641430a2036672aa0626bca66e71e6aad7ba6758abf4271df73635fdc,30001a7578c0efcc23a4de0a082794619dcfaa78071d54e0cdc684c4c916562c,14914203a175a4b0ade0119b23188c58a97f8dc4dfe2414e86519d812ede0303,024ed1dca7f18ca67a9370f93db66c08117503d86426ad3fc973d61d6d3bd49d,19f45e6d718d1b7e93fbf397dbe47daf1f5ec9ffdd705392e2ebbef8d0bb012a,282b1ccd8823e7b3e62d031ce11216347af5a68b5f0219f1c4aa2bb136413030,22af2f34e42e134b50119a8e69317706740301cb0992daef63f37a320675f9cc,2b3449c0e661dfc71e491002cbe828e3fb746777a80c8417c11d3c5664da3779,045da2e85c941fbcc9a5ec4e39d1664a50a417afb81e3ecf6ac98e883f73e718,1694c52c456771424433f4e6456223edb91863e9ed4fc177cd9708d74f0c05a4,01a2c7ebf5ac55c1134145d5a25879957f77ad7c5ff5d830e95d2cded1bff837,306062fa2e55d95eace092c61b1861ac70a0d9f444e98095433da2cf4af62c5b,10c195f1c6456ef6bc62eed98ed8d2d8c2a2a34cd6f6a38dc01b0fa3adc7d798,2bf01d26d6d9d962e39e0de82dc29a7ae54e2410497d04da3f92ade75d8ac210,11f3d4c0934a22bf13f1876c94de60e2ed365e58694f236462d9cbca352ed1d1,06a1506db653b2c73039fb3cc66ede2f542c97b362d0bc64b760bf27cdbe5e45,1c2a8378fd0cf16d88222c2ef8631876c6a744771b074cc4d93abeba98f79160,00ea34834a54c8f56b9033449b1b85684942bcb4ee7cd5e5d4ccaa5c24a31915,2a5b5653089f72b0c45e027c89a32b02058dbdcc8011af46f4013da582ba9bc3,080186e3162327954e0443677e99dd7974326f4759c98b309813221d8fb59834|0|5f8a6aec174628feaa7ab6c02cf3c5af57b09be4db81bf0bb1435d703168b623|b8e4bb9e04471b022f925db89d32a183e07e2efe0a45e2cfc19c10399bf7b515|f31eaf6d94eefdb902b6ac0d4a1d6cc3c271504f280483dacac484f131b0f218|531c847a97bf493b68f149d1d42ad0bdd03e5c956733d16dc9e73d7766b3c819 diff --git a/testdata/zolt-arith-diff/pairing/generator_cases.txt b/testdata/zolt-arith-diff/pairing/generator_cases.txt new file mode 100644 index 00000000..ccd8f9f5 --- /dev/null +++ b/testdata/zolt-arith-diff/pairing/generator_cases.txt @@ -0,0 +1,7 @@ +# name|g1_scalar_u64|g2_scalar_u64|expected_fp12_le_hex +case_0|0|1|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +case_1|1|0|010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +case_2|1|1|950e879d73631f5eb5788589eb5f7ef8d63e0a28de1ba00dfe4ca9ed3f252b264a8afb8eb4349db466ed1809ea4d7c39bdab7938821f1b0a00a295c72c2de002e01dbdfd0254134efcb1ec877395d25f937719b344adb1a58d129be2d6f2a9132b16a16e8ab030b130e69c69bd20b4c45986e6744a98314b5c1a0f50faa90b04dbaf9ef8aeeee3f50be31c210b598f4752f073987f9d35be8f6770d83f2ffc0af0d18dd9d2dbcdf943825acc12a7a9ddca45e629d962c6bd64908c3930a5541cfe2924dcc5580d5cef7a4bfdec90a91b59926f850d4a7923c01a5a5dbf0f5c094a2b9fb9d415820fa6b40c59bb9eade9c953407b0fc11da350a9d872cad6d3142974ca385854afdf5f583c04231adc5957c8914b6b20dc89660ed7c3bbe7c01d972be2d53ecdb27a1bcc16ac610db95aa7d237c8ff55a898cb88645a0e32530b23d7ebf5dafdd79b0f9c2ac4ba07ce18d3d16cf36e47916c4cae5d08d3afa813972c769e8514533e380c9443b3e1ee5c96fa3a0a73f301b626454721527bf900 +case_3|2|3|a28d573b72cd18dd69d75fbde33122a827173dfee47992577b6ed860494d170d9a2cf587efa38f27a883a6ff6c585502aa2cc2b1b2ceb6c96c37045c76f2750318324928b7b0f825bc8841c06fddd134ed7a6f55f015dc2d8bc9ed41bbc4d10885d103ceedc73bcdb62ed90bfeb0a9b8d0be69530cfc0cfebbaae66eb755a714a3451756a6ff6659adf985eb42859415734974e59141ad9c9092af422fc2c011406163bab4291022908359e7de047c82678bf85559dd535ecd7ac87ccb2f56238565cc8dfab75c7e1f0393196409f075fa3eeefc0c565db58bcdb5cc8438e625e7ab34cbcd5be256c170b4fd5fe2a3dd2559f95acb9a0c00a3b147da6084490c1a845e74252f1443f52259087865c9219262d5ebd1a171038242bd30c1b1d52511c7068478d244fd651d68bc1bb3fd2c47ae43a313cf3041155413725fe62325902530eb1585505967a8044427bc1ace45092bd88f04e26665d358af05f0092658da3c5cdb2da1bfdd4bdeb12640b28f7f23b339a5084cfcd597ace6a39bbe2d +case_4|5|7|2e8d72665ad5bb89cf47012e28901b978ffd931b8f7d0f242b8091381e42af00ebbd572ead64f291e6b52090beb6d3886f502e99292b30f2e8633c64f2b6bd217f0d9e2374bb2c64245795237d5516281a359f39a33757b60122c1c6be1286144ea1f8fc07c163b3dedd5b154d16769aebb60c51a81888373bfee2433f172f1fb3ee6663e6511a91df0282031c74b315b2172b6b124d616062072904e095432a378812220e033501364dd69646c13551bbd801b63568f7c01c5d3db8cae9ff2cf5dff9e8f24bbfd3b3e56d51441bab94e840b93ce6ed943556a3515047dd982faea0f8377b9bf5a1a8cf209c68632517df6500d6660384f68a21cecd8602c20b2f39502ebea2ef018c8de57d045e59e338870c12d2cc962778a40dec25bb1f172a84a5807fd2c69ef76d430d747f08b448b7fadfb94705607b07518495a3cd0dbe2d6d64bd4723523faf48da176090fa5a207f1e5f76acd24be46be790f2b2102be9928ee2f75c881c74649586bb6b0bdd4d67bd2d82ec32cc2962111e167b12 +case_5|17|19|ca2a56bb4db843689f2e2d79403ef6f3a753dc0dd4d233efeb3215a4f17a1a17bf063436a9e405070461d09c792bf69bec2fd6dff3d6e90560e85ee938c92e100d98c1f4e6fb05ad6f29f686a3f6d3adcf129d606ed2193fb5b22a7fe8f6840182c03d1123c59da35c78ef8b42ed4d5c85b86e22e80cc1b66bddca96e6c5e017db518b216dd4b71a88e4524a80be8ad14dbc5ae252575caa2fcd10ac5d82ae2750ab6b58d54d1c3fad400b137ca479dd5ffc2e195f09097a36ffd0361083780ad9d221a38c90cf1f360c429e34d0343fa6062d26d0d9b9cd5831369f3cd0ff18846ecdffb5a32eb8f9a9b2da0ec1fc9ff0f9553755d7abafc0b31aba51cef4141ceb38322d270eded49003e7eb7892d0c52f484ba2c8b92f9e97d23e34b36f14b6da2c0c74db3e3711fd28c16300a02369ee26b5d59cd0a1204d3daf15d7ae095ba547f4c49dee6cfe9ab9764f3bb727e01f8abc62ce8be9ca55d12c984c91151563b311bfc1377b99ac33e86c81075d270da1438492ec394a75844199137020 diff --git a/testdata/zolt-arith-diff/point_compression/g1_compress.txt b/testdata/zolt-arith-diff/point_compression/g1_compress.txt new file mode 100644 index 00000000..04be405d --- /dev/null +++ b/testdata/zolt-arith-diff/point_compression/g1_compress.txt @@ -0,0 +1,9 @@ +# name|uncompressed_x_le|uncompressed_y_le|compressed_hex +identity|||0000000000000000000000000000000000000000000000000000000000000040 +generator|0100000000000000000000000000000000000000000000000000000000000000|0200000000000000000000000000000000000000000000000000000000000000|0100000000000000000000000000000000000000000000000000000000000000 +double|d3cf876dc108c2d3a81c8716a91678d9851518685b04859b021a132ee7440603|c4a2185a7abf3effc78f53e349a4a6680a9caeb2965f84e7927c0a0e8c73ed15|d3cf876dc108c2d3a81c8716a91678d9851518685b04859b021a132ee7440603 +scalar_42|343f9b2f88177d5317c5e46d58775535fbf727aefd9a8f7cd71f97b65df38809|03b24c3b2cdb2cd50599f495aea8b46028d56d6e0a390770c6c8af3fa6ffba23|343f9b2f88177d5317c5e46d58775535fbf727aefd9a8f7cd71f97b65df38889 +random_0|85d5302458b3bf320d67ec67a4d2c2e8616b9af5c732e873db7da333e98ae825|5900c9762be113984fba18f842ab80323bef3feec8a2159d60391230a492e42c|85d5302458b3bf320d67ec67a4d2c2e8616b9af5c732e873db7da333e98ae8a5 +random_1|b670e60828df3ea8bad9d4469de088178a0304f4551fc24b80993aef0048c513|3c807872da768bc5dc063cbf2c2fd8cc70e25c538bc293d791b1004407349516|b670e60828df3ea8bad9d4469de088178a0304f4551fc24b80993aef0048c513 +random_2|8810f677d97bcfba5cd8ff481010115cf22e11024fe21afbfe7a5ca00a3f3b2f|ea41281340a0d59c09c6ff9a2c1df929838aeb86061a2707069a362dde195613|8810f677d97bcfba5cd8ff481010115cf22e11024fe21afbfe7a5ca00a3f3b2f +random_3|e2b172dcd926cb8e3241c599c234828766c253a1b5a271dc9b088799bf6e2728|269dca0e9d72f23ced0a58eef880ca3439651286e68876b740140fc8e7557f21|e2b172dcd926cb8e3241c599c234828766c253a1b5a271dc9b088799bf6e27a8 diff --git a/testdata/zolt-arith-diff/point_compression/g2_compress.txt b/testdata/zolt-arith-diff/point_compression/g2_compress.txt new file mode 100644 index 00000000..a0155c86 --- /dev/null +++ b/testdata/zolt-arith-diff/point_compression/g2_compress.txt @@ -0,0 +1,9 @@ +# name|x_c0_le|x_c1_le|y_c0_le|y_c1_le|compressed_hex +identity|||||00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040 +generator|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018|c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19|aa7dfa6601cce64c7bd3430c69e7d1e38f40cb8d8071ab4aeb6d8cdba55ec812|5b9722d1dcdaac55f38eb37033314bbc95330c69ad999eec75f05f58d0890609|edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19 +double|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc27|79ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203e20|2e5d2b12ad6d2a6e46c0b1e64f9ba5440983c4422737bca0925f7e97b853bb04|52e19d50f085e198d448df4e6b5605359d573139158c2b72637482b7a58a5e19|b9b3b4620913f849ee2aa6a9cfd35c9d146f3e7c27596cc3e8d311fd3472dc2779ad28398ced57998435d8c63164b86d7033733ab82101b6379bf1b45d203ea0 +scalar_42|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11|919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097412|72c1c48de52409d357618aac72fcbe14cf59f05162b5449630b6772e04416407|db41a40b7cd0c6a84e832bd28bc179c9c5ac040dd0ed7d4a6bf8e516982d2225|e7e6af991b936dd662caae0472ba13801c5f3aa3ad44863d0f090d9ac8a86d11919a5606733a9e0630a5e2d0b1670de93ce8cc6fb0496a7bb71596ba34097492 +random_0|514b3b5ec84dacbb8b474c3f22adf9ad301c664f281dd02c99babed09920d40e|303908414ff32aac7289eb655ba7ba9ea31f489e912e734e51005da548b5b112|b35cf96fb5a9459089d446ac0d6d83ce6a87ad617d8ce7c5d82b43bcb69c1910|78152fa1d65c5787a8b876888cac1b30188da6d75581b870b21005753c9cc917|514b3b5ec84dacbb8b474c3f22adf9ad301c664f281dd02c99babed09920d40e303908414ff32aac7289eb655ba7ba9ea31f489e912e734e51005da548b5b112 +random_1|53de77c9866fcded2ea83c159ae4c071a6d5722e5d5919e1b3bcbb0f03e84a2f|9352d823aed9d26e3393665ea933dbe2e5d3a08de46802d12f0c14457a3cae04|600487e508891030b82144cd7d2d08f4f9d43b1a005ed8ca8c568aeb6dd9a927|ef8f7b174a15d83bb0769ef48558a2f7847f7abcdf44a7e2a02daab6670ca602|53de77c9866fcded2ea83c159ae4c071a6d5722e5d5919e1b3bcbb0f03e84a2f9352d823aed9d26e3393665ea933dbe2e5d3a08de46802d12f0c14457a3cae04 +random_2|8dfe9801a847767875b5ba27e0b768b96f377e9ecb26a41aa451136341c5cc2e|728dc039a18cf399845c61e75e784db5173f670f7089fad7d81c7b228c5af00e|1e8915ca3c172976a9476090db85b55aa00af6130b3d3774122e6c5a7587f025|e0fa844a436efeaf85702bb4602abfd919784bcf839725f5e06b144627e2210f|8dfe9801a847767875b5ba27e0b768b96f377e9ecb26a41aa451136341c5cc2e728dc039a18cf399845c61e75e784db5173f670f7089fad7d81c7b228c5af00e +random_3|c3f838751f55e293a0e9a7846c8c28b38f6eaa571ed044c5bec43834c5b6a00e|7f25461c368b50e773c17333355699748933ce7a0e136ca3fe321cb9e1503324|d15d1c8a2465d0b0c7efde8ad97ddab37505055c72eb2df11b09e9618874e517|eb3c60c59d91e575a707d3fcc82d0e54c23a55da52af473b08743db1d152501e|c3f838751f55e293a0e9a7846c8c28b38f6eaa571ed044c5bec43834c5b6a00e7f25461c368b50e773c17333355699748933ce7a0e136ca3fe321cb9e15033a4 diff --git a/testdata/zolt-arith-diff/transcript/challenge_vectors.txt b/testdata/zolt-arith-diff/transcript/challenge_vectors.txt new file mode 100644 index 00000000..aad1cffe --- /dev/null +++ b/testdata/zolt-arith-diff/transcript/challenge_vectors.txt @@ -0,0 +1,7 @@ +# name|init_label|ops_desc|expected_u128|expected_limb2_hex|expected_limb3_hex +u128_after_label|chal_test|append_label:data|162254257622815994855625205374327650453|-|- +scalar128_after_label|chal_test|append_label:data|-|904fe9fdafb2f895|1a110888b571b1d2 +u128_after_scalar|chal_test|append_scalar:input:42|320459821532379581560418381073129669534|-|- +scalar128_after_scalar|chal_test|append_scalar:input:42|-|92bd5ffbb384039e|1116510e6a7bab8a +u128_multi_step|multi_chal|append_label:round1;append_u64:size:256|291277234265759587083847672085601804209|-|- +scalar128_multi_step|multi_chal|append_label:round1;append_u64:size:256|-|611556ef099b5fb1|1b21f46b172ec1eb diff --git a/testdata/zolt-arith-diff/transcript/state_vectors.txt b/testdata/zolt-arith-diff/transcript/state_vectors.txt new file mode 100644 index 00000000..90bd1f9d --- /dev/null +++ b/testdata/zolt-arith-diff/transcript/state_vectors.txt @@ -0,0 +1,12 @@ +# name|init_label|ops_desc|expected_state_hex|expected_rounds +init_basic|init_test|-|40f6e61cb828bcfbd5143a3df302021d6ab7f85dad9d083b274537ba1e7391cd|0 +init_empty||-|89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3|0 +label_data|pub_test|append_label:data|8902d97269e4936d2e9e2cadd9b614ec812147e851a45c6bc06baa70683bcdeb|1 +label_hello|zolt_test|append_label:hello|3a2586879f2c7b911e0f006e6c3cc9ee03507c28b818138283508b192d43168c|1 +u64_999|pub_test|append_u64:count:999|04a7ea28820e22af92cf2f1b541f7b0e7c19694845fc8ac9e61feaac599e44eb|2 +u64_zero|pub_test|append_u64:size:0|8662f454e0f86b29483649ec9c1c80a0bb1e0c6ced13fe19a5c9747cca15e6ae|2 +scalar_7|pub_test|append_scalar:val:7|6bce2e0c05730ec8a53a55eb7debc95c724b2901dbfcb3751d3e4d4073808d53|2 +scalar_42|scalar_test|append_scalar:x:42|d4d30d42d0420bb6f03b5a2216b03b5b875646dcb42498dbcafed1007055a565|2 +scalar_zero|scalar_test|append_scalar:x:0|72de3108775c0d2f832785d10a4aae8e9fa1009509e45af863221470aa54ee6d|2 +multi_pub_3ops|sequence|append_label:step1;append_scalar:x:100;append_u64:n:42|f41a215806d7728873643b96e4b5b3e5aeb41dc66e9f3300734723d6b15e4307|5 +multi_labels_scalar|multi|append_label:round1;append_label:round2;append_scalar:r:999|442913f49360528ddee438c5debab02b86458fd7ed2cf5d389b867ab5ccd858e|4 diff --git a/tools/zolt-arith-diff/README.md b/tools/zolt-arith-diff/README.md new file mode 100644 index 00000000..87539adb --- /dev/null +++ b/tools/zolt-arith-diff/README.md @@ -0,0 +1,50 @@ +# Zolt-Arith Differential Workflow + +This is an optional, repo-level workflow for `zolt-arith` differential verification. + +It is intentionally kept outside `packages/zolt-arith/` so: + +- `cd packages/zolt-arith && zig build test` stays lightweight +- Rust tooling is not required for normal package builds +- CI can run differential verification in dedicated jobs only + +## What It Does + +- `arkworks-fixtures/` + Generates deterministic BN254 differential fixtures for: + - field ops (add, sub, mul, inverse for Fr and Fp) + - accumulator ops (sumOfProducts, batchInverse, mulU64, mulU128) + - pairing on generator-derived points + - MSM on deterministic doubled bases (G1 Fr, G1 i128, G2 Fr) + - Blake2b transcript state and challenge vectors (independent Rust oracle) + +- `check.zig` + Reads the generated fixtures and verifies Zolt against them. + +## Commands + +Generate fixtures: + +```bash +zig build gen-zolt-arith-diff-fixtures +``` + +Run optional differential tests: + +```bash +zig build test-zolt-arith-diff +``` + +Run both in one shot: + +```bash +tools/zolt-arith-diff/run.sh +``` + +## CI Shape + +Recommended dedicated jobs: + +- one job that regenerates fixtures and fails on drift +- one job that runs `zig build test-zolt-arith-diff` +- one job that runs `zig build bench-zolt-arith-field` and publishes results diff --git a/tools/zolt-arith-diff/arkworks-fixtures/Cargo.lock b/tools/zolt-arith-diff/arkworks-fixtures/Cargo.lock new file mode 100644 index 00000000..bfc022b9 --- /dev/null +++ b/tools/zolt-arith-diff/arkworks-fixtures/Cargo.lock @@ -0,0 +1,535 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "ahash", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "arrayvec", + "digest", + "educe", + "itertools", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "ahash", + "ark-ff", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "arrayvec", + "digest", + "num-bigint", + "rayon", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "git+https://github.com/a16z/arkworks-algebra?branch=dev%2Ftwist-shout#76bb3a4518928f1ff7f15875f940d614bb9845e6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", + "rayon", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zolt-arith-arkworks-fixtures" +version = "0.1.0" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "blake2", + "sha3", +] + +[[patch.unused]] +name = "allocative" +version = "0.3.4" +source = "git+https://github.com/facebookexperimental/allocative?rev=85b773d85d526d068ce94724ff7a7b81203fc95e#85b773d85d526d068ce94724ff7a7b81203fc95e" diff --git a/tools/zolt-arith-diff/arkworks-fixtures/Cargo.toml b/tools/zolt-arith-diff/arkworks-fixtures/Cargo.toml new file mode 100644 index 00000000..47c0ce1d --- /dev/null +++ b/tools/zolt-arith-diff/arkworks-fixtures/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "zolt-arith-arkworks-fixtures" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "zolt-arith-arkworks-fixtures" +path = "src/main.rs" + +[dependencies] +blake2 = "0.10" +ark-bn254 = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout" } +ark-ec = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout", default-features = false, features = ["parallel"] } +ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout", default-features = false, features = ["asm"] } +ark-serialize = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout", default-features = false } +ark-std = { version = "0.5" } +sha3 = "0.10" + +[patch.crates-io] +ark-bn254 = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout" } +ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout" } +ark-ec = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout" } +ark-serialize = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout" } +allocative = { git = "https://github.com/facebookexperimental/allocative", rev = "85b773d85d526d068ce94724ff7a7b81203fc95e" } + +[profile.release] +opt-level = 3 +lto = "fat" diff --git a/tools/zolt-arith-diff/arkworks-fixtures/src/main.rs b/tools/zolt-arith-diff/arkworks-fixtures/src/main.rs new file mode 100644 index 00000000..3b8c58e2 --- /dev/null +++ b/tools/zolt-arith-diff/arkworks-fixtures/src/main.rs @@ -0,0 +1,1013 @@ +mod transcript; + +use ark_bn254::{Bn254, Fq, Fq2, Fq6, Fq12, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; +use ark_ec::{ + pairing::Pairing, + scalar_mul::variable_base::{msm_i128, VariableBaseMSM}, + AdditiveGroup, AffineRepr, CurveGroup, PrimeGroup, +}; +use ark_ff::{BigInteger, CyclotomicMultSubgroup, Field, PrimeField, UniformRand, Zero, One}; +use ark_serialize::CanonicalSerialize; +use ark_std::rand::{rngs::StdRng, SeedableRng}; +use sha3::{Sha3_256, Digest}; +use std::{ + env, fs, + path::{Path, PathBuf}, +}; + +fn hex_le(value: &F) -> String { + let mut bytes = value.into_bigint().to_bytes_le(); + bytes.resize(32, 0); + hex_encode(&bytes) +} + +fn hex_be(value: &F) -> String { + let mut bytes = value.into_bigint().to_bytes_be(); + if bytes.len() < 32 { + let mut padded = vec![0u8; 32 - bytes.len()]; + padded.extend_from_slice(&bytes); + bytes = padded; + } + hex_encode(&bytes) +} + +fn hex_encode(bytes: &[u8]) -> String { + const LUT: &[u8; 16] = b"0123456789abcdef"; + let mut out = String::with_capacity(bytes.len() * 2); + for &byte in bytes { + out.push(LUT[(byte >> 4) as usize] as char); + out.push(LUT[(byte & 0x0f) as usize] as char); + } + out +} + +fn fq12_hex_le(value: &Fq12) -> String { + let coords = [ + &value.c0.c0.c0, + &value.c0.c0.c1, + &value.c0.c1.c0, + &value.c0.c1.c1, + &value.c0.c2.c0, + &value.c0.c2.c1, + &value.c1.c0.c0, + &value.c1.c0.c1, + &value.c1.c1.c0, + &value.c1.c1.c1, + &value.c1.c2.c0, + &value.c1.c2.c1, + ]; + + let mut bytes = Vec::with_capacity(384); + for coord in coords { + let mut le = coord.into_bigint().to_bytes_le(); + le.resize(32, 0); + bytes.extend_from_slice(&le); + } + hex_encode(&bytes) +} + +fn fq2_hex_le(value: &Fq2) -> String { + let coords = [&value.c0, &value.c1]; + let mut bytes = Vec::with_capacity(64); + for coord in coords { + let mut le = coord.into_bigint().to_bytes_le(); + le.resize(32, 0); + bytes.extend_from_slice(&le); + } + hex_encode(&bytes) +} + +fn fq6_hex_le(value: &Fq6) -> String { + let coords = [ + &value.c0.c0, &value.c0.c1, + &value.c1.c0, &value.c1.c1, + &value.c2.c0, &value.c2.c1, + ]; + let mut bytes = Vec::with_capacity(192); + for coord in coords { + let mut le = coord.into_bigint().to_bytes_le(); + le.resize(32, 0); + bytes.extend_from_slice(&le); + } + hex_encode(&bytes) +} + +fn write_file(path: PathBuf, contents: String) { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent).expect("create parent dirs"); + } + fs::write(path, contents).expect("write fixture file"); +} + +fn generate_g1_bases(n: usize) -> Vec { + let gen = G1Affine::generator(); + let mut proj = G1Projective::from(gen); + let mut bases = Vec::with_capacity(n); + for _ in 0..n { + bases.push(proj.into_affine()); + proj = proj.double(); + } + bases +} + +fn generate_g2_bases(n: usize) -> Vec { + let gen = G2Affine::generator(); + let mut proj = G2Projective::from(gen); + let mut bases = Vec::with_capacity(n); + for _ in 0..n { + bases.push(proj.into_affine()); + proj = proj.double(); + } + bases +} + +fn generate_field_ops(out_dir: &Path) { + let mut rng = StdRng::seed_from_u64(0x5eed_f11d_2540); + + let fr_cases = [ + (Fr::from(0u64), Fr::from(1u64)), + (Fr::from(1u64), Fr::from(2u64)), + (Fr::from(7u64), Fr::from(3u64)), + (Fr::rand(&mut rng), Fr::rand(&mut rng)), + (Fr::rand(&mut rng), Fr::rand(&mut rng)), + (Fr::rand(&mut rng), Fr::rand(&mut rng)), + ]; + + let fq_cases = [ + (Fq::from(0u64), Fq::from(1u64)), + (Fq::from(1u64), Fq::from(2u64)), + (Fq::from(11u64), Fq::from(19u64)), + (Fq::rand(&mut rng), Fq::rand(&mut rng)), + (Fq::rand(&mut rng), Fq::rand(&mut rng)), + (Fq::rand(&mut rng), Fq::rand(&mut rng)), + ]; + + let mut fr_out = String::from("# op|a_be_hex|b_be_hex|expected_be_hex\n"); + for (a, b) in fr_cases { + fr_out.push_str(&format!("add|{}|{}|{}\n", hex_be(&a), hex_be(&b), hex_be(&(a + b)))); + fr_out.push_str(&format!("sub|{}|{}|{}\n", hex_be(&a), hex_be(&b), hex_be(&(a - b)))); + fr_out.push_str(&format!("mul|{}|{}|{}\n", hex_be(&a), hex_be(&b), hex_be(&(a * b)))); + if !a.is_zero() { + fr_out.push_str(&format!("inv|{}|-|{}\n", hex_be(&a), hex_be(&a.inverse().unwrap()))); + } + } + + let mut fq_out = String::from("# op|a_be_hex|b_be_hex|expected_be_hex\n"); + for (a, b) in fq_cases { + fq_out.push_str(&format!("add|{}|{}|{}\n", hex_be(&a), hex_be(&b), hex_be(&(a + b)))); + fq_out.push_str(&format!("sub|{}|{}|{}\n", hex_be(&a), hex_be(&b), hex_be(&(a - b)))); + fq_out.push_str(&format!("mul|{}|{}|{}\n", hex_be(&a), hex_be(&b), hex_be(&(a * b)))); + if !a.is_zero() { + fq_out.push_str(&format!("inv|{}|-|{}\n", hex_be(&a), hex_be(&a.inverse().unwrap()))); + } + } + + write_file(out_dir.join("field/fr_ops.txt"), fr_out); + write_file(out_dir.join("field/fp_ops.txt"), fq_out); +} + +fn generate_pairing_cases(out_dir: &Path) { + let cases: &[(u64, u64)] = &[(0, 1), (1, 0), (1, 1), (2, 3), (5, 7), (17, 19)]; + let g1_gen = G1Affine::generator(); + let g2_gen = G2Affine::generator(); + + let mut out = String::from("# name|g1_scalar_u64|g2_scalar_u64|expected_fp12_le_hex\n"); + for (index, (g1_scalar, g2_scalar)) in cases.iter().enumerate() { + let g1 = G1Projective::from(g1_gen) + .mul_bigint(Fr::from(*g1_scalar).into_bigint()) + .into_affine(); + let g2 = G2Projective::from(g2_gen) + .mul_bigint(Fr::from(*g2_scalar).into_bigint()) + .into_affine(); + let expected = Bn254::pairing(g1, g2).0; + out.push_str(&format!( + "case_{index}|{g1_scalar}|{g2_scalar}|{}\n", + fq12_hex_le(&expected) + )); + } + + write_file(out_dir.join("pairing/generator_cases.txt"), out); +} + +fn format_g1_point(point: G1Affine) -> (u8, String, String) { + if point.infinity { + return (1, String::new(), String::new()); + } + (0, hex_le(&point.x), hex_le(&point.y)) +} + +fn format_g2_point(point: G2Affine) -> (u8, String, String, String, String) { + if point.infinity { + return (1, String::new(), String::new(), String::new(), String::new()); + } + ( + 0, + hex_le(&point.x.c0), + hex_le(&point.x.c1), + hex_le(&point.y.c0), + hex_le(&point.y.c1), + ) +} + +fn join_field_scalars(values: &[Fr]) -> String { + values.iter().map(hex_be).collect::>().join(",") +} + +fn join_i128_scalars(values: &[i128]) -> String { + values.iter().map(|v| v.to_string()).collect::>().join(",") +} + +fn generate_i128_scalars(n: usize) -> Vec { + let mut seed: u64 = 0xcafebabe; + (0..n) + .map(|_| { + seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407); + let magnitude = (seed & 0x7fff_ffff_ffff_ffff) as i128; + if (seed & 1) == 0 { + magnitude + } else { + -magnitude + } + }) + .collect() +} + +fn generate_msm_cases(out_dir: &Path) { + let g1_sizes = [1usize, 2, 4, 8, 16]; + let g2_sizes = [1usize, 2, 4, 8, 16, 32]; + let max_g1 = *g1_sizes.iter().max().unwrap(); + let max_g2 = *g2_sizes.iter().max().unwrap(); + let max_n = std::cmp::max(max_g1, max_g2); + + let g1_bases = generate_g1_bases(max_g1); + let g2_bases = generate_g2_bases(max_g2); + + let mut rng = StdRng::seed_from_u64(0x51ca_4f11); + let fr_scalars: Vec = (0..max_n).map(|_| Fr::rand(&mut rng)).collect(); + let i128_scalars = generate_i128_scalars(max_g1); + + let mut g1_fr_out = String::from("# name|scalars_be_hex_csv|expected_infinity|expected_x_le_hex|expected_y_le_hex\n"); + for (index, &n) in g1_sizes.iter().enumerate() { + let expected = G1Projective::msm(&g1_bases[..n], &fr_scalars[..n]).unwrap().into_affine(); + let (inf, x, y) = format_g1_point(expected); + g1_fr_out.push_str(&format!( + "case_{index}|{}|{inf}|{x}|{y}\n", + join_field_scalars(&fr_scalars[..n]) + )); + } + + let mut g1_i128_out = String::from("# name|scalars_i128_csv|expected_infinity|expected_x_le_hex|expected_y_le_hex\n"); + for (index, &n) in g1_sizes.iter().enumerate() { + let expected = msm_i128::(&g1_bases[..n], &i128_scalars[..n], true).into_affine(); + let (inf, x, y) = format_g1_point(expected); + g1_i128_out.push_str(&format!( + "case_{index}|{}|{inf}|{x}|{y}\n", + join_i128_scalars(&i128_scalars[..n]) + )); + } + + let mut g2_fr_out = String::from( + "# name|scalars_be_hex_csv|expected_infinity|expected_x_c0_le_hex|expected_x_c1_le_hex|expected_y_c0_le_hex|expected_y_c1_le_hex\n", + ); + for (index, &n) in g2_sizes.iter().enumerate() { + let expected = G2Projective::msm(&g2_bases[..n], &fr_scalars[..n]).unwrap().into_affine(); + let (inf, x0, x1, y0, y1) = format_g2_point(expected); + g2_fr_out.push_str(&format!( + "case_{index}|{}|{inf}|{x0}|{x1}|{y0}|{y1}\n", + join_field_scalars(&fr_scalars[..n]) + )); + } + + write_file(out_dir.join("msm/g1_fr_cases.txt"), g1_fr_out); + write_file(out_dir.join("msm/g1_i128_cases.txt"), g1_i128_out); + write_file(out_dir.join("msm/g2_fr_cases.txt"), g2_fr_out); +} + +fn generate_accumulator_ops(out_dir: &Path) { + let mut rng = StdRng::seed_from_u64(0xacc0_0001); + + // --- sum_of_products: a0*b0 + a1*b1 --- + let sop_cases: Vec<(Fr, Fr, Fr, Fr)> = vec![ + (Fr::from(0u64), Fr::from(1u64), Fr::from(0u64), Fr::from(1u64)), + (Fr::from(1u64), Fr::from(1u64), Fr::from(1u64), Fr::from(1u64)), + (Fr::from(3u64), Fr::from(5u64), Fr::from(7u64), Fr::from(11u64)), + (Fr::rand(&mut rng), Fr::rand(&mut rng), Fr::rand(&mut rng), Fr::rand(&mut rng)), + (Fr::rand(&mut rng), Fr::rand(&mut rng), Fr::rand(&mut rng), Fr::rand(&mut rng)), + (Fr::rand(&mut rng), Fr::rand(&mut rng), Fr::rand(&mut rng), Fr::rand(&mut rng)), + ]; + let mut sop_out = String::from("# name|a0_be_hex|b0_be_hex|a1_be_hex|b1_be_hex|expected_be_hex\n"); + for (i, (a0, b0, a1, b1)) in sop_cases.iter().enumerate() { + let expected = *a0 * *b0 + *a1 * *b1; + sop_out.push_str(&format!( + "case_{i}|{}|{}|{}|{}|{}\n", + hex_be(a0), hex_be(b0), hex_be(a1), hex_be(b1), hex_be(&expected) + )); + } + write_file(out_dir.join("accumulator/sum_of_products.txt"), sop_out); + + // --- batch_inverse --- + let mut bi_out = String::from("# name|count|inputs_be_hex_csv|expected_be_hex_csv\n"); + let batch_sizes = [1usize, 2, 4, 8]; + let mut bi_rng = StdRng::seed_from_u64(0xacc0_0002); + for (i, &n) in batch_sizes.iter().enumerate() { + let inputs: Vec = (0..n).map(|_| Fr::rand(&mut bi_rng)).collect(); + let outputs: Vec = inputs.iter().map(|x| x.inverse().unwrap()).collect(); + bi_out.push_str(&format!( + "case_{i}|{n}|{}|{}\n", + join_field_scalars(&inputs), + join_field_scalars(&outputs), + )); + } + // Case with a zero element (zero inverse → zero) + { + let inputs = vec![Fr::rand(&mut bi_rng), Fr::from(0u64), Fr::rand(&mut bi_rng)]; + let outputs: Vec = inputs + .iter() + .map(|x| { + if x.is_zero() { + Fr::from(0u64) + } else { + x.inverse().unwrap() + } + }) + .collect(); + bi_out.push_str(&format!( + "case_with_zero|3|{}|{}\n", + join_field_scalars(&inputs), + join_field_scalars(&outputs), + )); + } + write_file(out_dir.join("accumulator/batch_inverse.txt"), bi_out); + + // --- mul_u64: field * u64 scalar --- + let mut mu64_rng = StdRng::seed_from_u64(0xacc0_0003); + let mu64_cases: Vec<(Fr, u64)> = vec![ + (Fr::rand(&mut mu64_rng), 0), + (Fr::rand(&mut mu64_rng), 1), + (Fr::rand(&mut mu64_rng), 2), + (Fr::rand(&mut mu64_rng), 12345), + (Fr::rand(&mut mu64_rng), 0xdeadbeef_cafebabe), + (Fr::rand(&mut mu64_rng), u64::MAX), + ]; + let mut mu64_out = String::from("# name|field_be_hex|scalar_u64|expected_be_hex\n"); + for (i, (field_val, scalar)) in mu64_cases.iter().enumerate() { + let expected = *field_val * Fr::from(*scalar); + mu64_out.push_str(&format!( + "case_{i}|{}|{scalar}|{}\n", + hex_be(field_val), + hex_be(&expected) + )); + } + write_file(out_dir.join("accumulator/mul_u64.txt"), mu64_out); + + // --- mul_u128: field * u128 scalar --- + let mut mu128_rng = StdRng::seed_from_u64(0xacc0_0004); + let mu128_cases: Vec<(Fr, u128)> = vec![ + (Fr::rand(&mut mu128_rng), 0), + (Fr::rand(&mut mu128_rng), 1), + (Fr::rand(&mut mu128_rng), u64::MAX as u128), + (Fr::rand(&mut mu128_rng), (u64::MAX as u128) + 1), + (Fr::rand(&mut mu128_rng), 0xdeadbeef_cafebabe_12345678_9abcdef0), + (Fr::rand(&mut mu128_rng), u128::MAX), + ]; + let mut mu128_out = String::from("# name|field_be_hex|scalar_u128|expected_be_hex\n"); + for (i, (field_val, scalar)) in mu128_cases.iter().enumerate() { + let expected = *field_val * Fr::from(*scalar); + mu128_out.push_str(&format!( + "case_{i}|{}|{scalar}|{}\n", + hex_be(field_val), + hex_be(&expected) + )); + } + write_file(out_dir.join("accumulator/mul_u128.txt"), mu128_out); +} + +fn generate_transcript_fixtures(out_dir: &Path) { + use transcript::Blake2bTranscript; + + // Uses only the PUBLIC transcript API (init, appendLabel, appendU64, + // appendScalar, challengeU128, challengeScalar128Bits) so that the Zig + // differential verifier can exercise the same paths without accessing + // private/raw methods. + + // --- State vectors --- + // Each line: name|init_label|ops_desc|expected_state_hex|expected_rounds + // ops_desc uses semicolons to separate steps, colons for args: + // append_label:data ; append_u64:count:999 ; append_scalar:val:7 + let mut state_out = String::from( + "# name|init_label|ops_desc|expected_state_hex|expected_rounds\n", + ); + + let emit = |out: &mut String, name: &str, t: &Blake2bTranscript, init_label: &str, ops: &str| { + out.push_str(&format!( + "{name}|{init_label}|{ops}|{}|{}\n", + hex_encode(t.state()), + t.n_rounds() + )); + }; + + // init only + { + let t = Blake2bTranscript::new(b"init_test"); + emit(&mut state_out, "init_basic", &t, "init_test", "-"); + } + { + let t = Blake2bTranscript::new(b""); + emit(&mut state_out, "init_empty", &t, "", "-"); + } + + // appendLabel + { + let mut t = Blake2bTranscript::new(b"pub_test"); + t.append_label(b"data"); + emit(&mut state_out, "label_data", &t, "pub_test", "append_label:data"); + } + { + let mut t = Blake2bTranscript::new(b"zolt_test"); + t.append_label(b"hello"); + emit(&mut state_out, "label_hello", &t, "zolt_test", "append_label:hello"); + } + + // appendU64 + { + let mut t = Blake2bTranscript::new(b"pub_test"); + t.append_u64(b"count", 999); + emit(&mut state_out, "u64_999", &t, "pub_test", "append_u64:count:999"); + } + { + let mut t = Blake2bTranscript::new(b"pub_test"); + t.append_u64(b"size", 0); + emit(&mut state_out, "u64_zero", &t, "pub_test", "append_u64:size:0"); + } + + // appendScalar + { + let mut t = Blake2bTranscript::new(b"pub_test"); + t.append_scalar(b"val", Fr::from(7u64)); + emit(&mut state_out, "scalar_7", &t, "pub_test", "append_scalar:val:7"); + } + { + let mut t = Blake2bTranscript::new(b"scalar_test"); + t.append_scalar(b"x", Fr::from(42u64)); + emit(&mut state_out, "scalar_42", &t, "scalar_test", "append_scalar:x:42"); + } + { + let mut t = Blake2bTranscript::new(b"scalar_test"); + t.append_scalar(b"x", Fr::from(0u64)); + emit(&mut state_out, "scalar_zero", &t, "scalar_test", "append_scalar:x:0"); + } + + // Multi-step (public API only) + { + let mut t = Blake2bTranscript::new(b"sequence"); + t.append_label(b"step1"); + t.append_scalar(b"x", Fr::from(100u64)); + t.append_u64(b"n", 42); + emit(&mut state_out, "multi_pub_3ops", &t, "sequence", + "append_label:step1;append_scalar:x:100;append_u64:n:42"); + } + { + let mut t = Blake2bTranscript::new(b"multi"); + t.append_label(b"round1"); + t.append_label(b"round2"); + t.append_scalar(b"r", Fr::from(999u64)); + emit(&mut state_out, "multi_labels_scalar", &t, "multi", + "append_label:round1;append_label:round2;append_scalar:r:999"); + } + + write_file(out_dir.join("transcript/state_vectors.txt"), state_out); + + // --- Challenge vectors --- + // Each line: name|init_label|ops_desc|expected_u128|expected_limb2_hex|expected_limb3_hex + let mut chal_out = String::from( + "# name|init_label|ops_desc|expected_u128|expected_limb2_hex|expected_limb3_hex\n", + ); + + // challenge after appendLabel + { + let mut t = Blake2bTranscript::new(b"chal_test"); + t.append_label(b"data"); + let val = t.challenge_u128(); + chal_out.push_str(&format!("u128_after_label|chal_test|append_label:data|{val}|-|-\n")); + } + { + let mut t = Blake2bTranscript::new(b"chal_test"); + t.append_label(b"data"); + let (low, high) = t.challenge_scalar_128bits(); + chal_out.push_str(&format!( + "scalar128_after_label|chal_test|append_label:data|-|{low:016x}|{high:016x}\n" + )); + } + + // challenge after appendScalar + { + let mut t = Blake2bTranscript::new(b"chal_test"); + t.append_scalar(b"input", Fr::from(42u64)); + let val = t.challenge_u128(); + chal_out.push_str(&format!( + "u128_after_scalar|chal_test|append_scalar:input:42|{val}|-|-\n" + )); + } + { + let mut t = Blake2bTranscript::new(b"chal_test"); + t.append_scalar(b"input", Fr::from(42u64)); + let (low, high) = t.challenge_scalar_128bits(); + chal_out.push_str(&format!( + "scalar128_after_scalar|chal_test|append_scalar:input:42|-|{low:016x}|{high:016x}\n" + )); + } + + // challenge after multi-step + { + let mut t = Blake2bTranscript::new(b"multi_chal"); + t.append_label(b"round1"); + t.append_u64(b"size", 256); + let val = t.challenge_u128(); + chal_out.push_str(&format!( + "u128_multi_step|multi_chal|append_label:round1;append_u64:size:256|{val}|-|-\n" + )); + } + { + let mut t = Blake2bTranscript::new(b"multi_chal"); + t.append_label(b"round1"); + t.append_u64(b"size", 256); + let (low, high) = t.challenge_scalar_128bits(); + chal_out.push_str(&format!( + "scalar128_multi_step|multi_chal|append_label:round1;append_u64:size:256|-|{low:016x}|{high:016x}\n" + )); + } + + write_file(out_dir.join("transcript/challenge_vectors.txt"), chal_out); +} + +fn generate_extension_field_ops(out_dir: &Path) { + let mut rng = StdRng::seed_from_u64(0xe47f_1e1d); + + // --- Fp2 --- + let fp2_cases: Vec<(Fq2, Fq2)> = vec![ + (Fq2::zero(), Fq2::one()), + (Fq2::one(), Fq2::one()), + (Fq2::new(Fq::from(3u64), Fq::from(0u64)), Fq2::new(Fq::from(0u64), Fq::from(7u64))), + (Fq2::new(Fq::from(5u64), Fq::from(11u64)), Fq2::new(Fq::from(13u64), Fq::from(17u64))), + (Fq2::rand(&mut rng), Fq2::rand(&mut rng)), + (Fq2::rand(&mut rng), Fq2::rand(&mut rng)), + (Fq2::rand(&mut rng), Fq2::rand(&mut rng)), + (Fq2::rand(&mut rng), Fq2::rand(&mut rng)), + ]; + + let mut fp2_out = String::from("# op|a_le_hex|b_le_hex|expected_le_hex\n"); + for (a, b) in &fp2_cases { + fp2_out.push_str(&format!("add|{}|{}|{}\n", fq2_hex_le(a), fq2_hex_le(b), fq2_hex_le(&(*a + *b)))); + fp2_out.push_str(&format!("sub|{}|{}|{}\n", fq2_hex_le(a), fq2_hex_le(b), fq2_hex_le(&(*a - *b)))); + fp2_out.push_str(&format!("mul|{}|{}|{}\n", fq2_hex_le(a), fq2_hex_le(b), fq2_hex_le(&(*a * *b)))); + fp2_out.push_str(&format!("square|{}|-|{}\n", fq2_hex_le(a), fq2_hex_le(&a.square()))); + if !a.is_zero() { + fp2_out.push_str(&format!("inv|{}|-|{}\n", fq2_hex_le(a), fq2_hex_le(&a.inverse().unwrap()))); + } + { + let mut conj = *a; + conj.conjugate_in_place(); + fp2_out.push_str(&format!("conjugate|{}|-|{}\n", fq2_hex_le(a), fq2_hex_le(&conj))); + } + } + write_file(out_dir.join("extensions/fp2_ops.txt"), fp2_out); + + // --- Fp6 --- + let fp6_cases: Vec<(Fq6, Fq6)> = vec![ + (Fq6::zero(), Fq6::one()), + (Fq6::one(), Fq6::one()), + (Fq6::new(Fq2::new(Fq::from(1u64), Fq::from(2u64)), + Fq2::new(Fq::from(3u64), Fq::from(4u64)), + Fq2::new(Fq::from(5u64), Fq::from(6u64))), + Fq6::new(Fq2::new(Fq::from(7u64), Fq::from(8u64)), + Fq2::new(Fq::from(9u64), Fq::from(10u64)), + Fq2::new(Fq::from(11u64), Fq::from(12u64)))), + (Fq6::rand(&mut rng), Fq6::rand(&mut rng)), + (Fq6::rand(&mut rng), Fq6::rand(&mut rng)), + (Fq6::rand(&mut rng), Fq6::rand(&mut rng)), + (Fq6::rand(&mut rng), Fq6::rand(&mut rng)), + (Fq6::rand(&mut rng), Fq6::rand(&mut rng)), + ]; + + let mut fp6_out = String::from("# op|a_le_hex|b_le_hex|expected_le_hex\n"); + for (a, b) in &fp6_cases { + fp6_out.push_str(&format!("add|{}|{}|{}\n", fq6_hex_le(a), fq6_hex_le(b), fq6_hex_le(&(*a + *b)))); + fp6_out.push_str(&format!("sub|{}|{}|{}\n", fq6_hex_le(a), fq6_hex_le(b), fq6_hex_le(&(*a - *b)))); + fp6_out.push_str(&format!("mul|{}|{}|{}\n", fq6_hex_le(a), fq6_hex_le(b), fq6_hex_le(&(*a * *b)))); + fp6_out.push_str(&format!("square|{}|-|{}\n", fq6_hex_le(a), fq6_hex_le(&a.square()))); + if !a.is_zero() { + fp6_out.push_str(&format!("inv|{}|-|{}\n", fq6_hex_le(a), fq6_hex_le(&a.inverse().unwrap()))); + } + } + write_file(out_dir.join("extensions/fp6_ops.txt"), fp6_out); + + // --- Fp12 --- + let fp12_cases: Vec<(Fq12, Fq12)> = vec![ + (Fq12::zero(), Fq12::one()), + (Fq12::one(), Fq12::one()), + (Fq12::new( + Fq6::new(Fq2::new(Fq::from(1u64), Fq::from(2u64)), + Fq2::new(Fq::from(3u64), Fq::from(4u64)), + Fq2::new(Fq::from(5u64), Fq::from(6u64))), + Fq6::new(Fq2::new(Fq::from(7u64), Fq::from(8u64)), + Fq2::new(Fq::from(9u64), Fq::from(10u64)), + Fq2::new(Fq::from(11u64), Fq::from(12u64)))), + Fq12::new( + Fq6::new(Fq2::new(Fq::from(13u64), Fq::from(14u64)), + Fq2::new(Fq::from(15u64), Fq::from(16u64)), + Fq2::new(Fq::from(17u64), Fq::from(18u64))), + Fq6::new(Fq2::new(Fq::from(19u64), Fq::from(20u64)), + Fq2::new(Fq::from(21u64), Fq::from(22u64)), + Fq2::new(Fq::from(23u64), Fq::from(24u64))))), + (Fq12::rand(&mut rng), Fq12::rand(&mut rng)), + (Fq12::rand(&mut rng), Fq12::rand(&mut rng)), + (Fq12::rand(&mut rng), Fq12::rand(&mut rng)), + (Fq12::rand(&mut rng), Fq12::rand(&mut rng)), + (Fq12::rand(&mut rng), Fq12::rand(&mut rng)), + ]; + + let mut fp12_out = String::from("# op|a_le_hex|b_le_hex|expected_le_hex\n"); + for (a, b) in &fp12_cases { + fp12_out.push_str(&format!("add|{}|{}|{}\n", fq12_hex_le(a), fq12_hex_le(b), fq12_hex_le(&(*a + *b)))); + fp12_out.push_str(&format!("sub|{}|{}|{}\n", fq12_hex_le(a), fq12_hex_le(b), fq12_hex_le(&(*a - *b)))); + fp12_out.push_str(&format!("mul|{}|{}|{}\n", fq12_hex_le(a), fq12_hex_le(b), fq12_hex_le(&(*a * *b)))); + fp12_out.push_str(&format!("square|{}|-|{}\n", fq12_hex_le(a), fq12_hex_le(&a.square()))); + if !a.is_zero() { + fp12_out.push_str(&format!("inv|{}|-|{}\n", fq12_hex_le(a), fq12_hex_le(&a.inverse().unwrap()))); + } + { + let mut conj = *a; + conj.conjugate_in_place(); + fp12_out.push_str(&format!("conjugate|{}|-|{}\n", fq12_hex_le(a), fq12_hex_le(&conj))); + } + fp12_out.push_str(&format!("frobenius|{}|-|{}\n", fq12_hex_le(a), fq12_hex_le(&a.frobenius_map(1)))); + fp12_out.push_str(&format!("frobenius2|{}|-|{}\n", fq12_hex_le(a), fq12_hex_le(&a.frobenius_map(2)))); + fp12_out.push_str(&format!("frobenius3|{}|-|{}\n", fq12_hex_le(a), fq12_hex_le(&a.frobenius_map(3)))); + if !a.is_zero() { + let mut cyc = *a; + cyc.cyclotomic_square_in_place(); + fp12_out.push_str(&format!("cyclotomic_square|{}|-|{}\n", fq12_hex_le(a), fq12_hex_le(&cyc))); + } + } + write_file(out_dir.join("extensions/fp12_ops.txt"), fp12_out); +} + +fn generate_g2_ops(out_dir: &Path) { + let mut rng = StdRng::seed_from_u64(0x6200_0001); + let gen = G2Affine::generator(); + + let mut out = String::from( + "# op|arg1_be_hex|arg2_be_hex|expected_infinity|expected_x_c0_le|expected_x_c1_le|expected_y_c0_le|expected_y_c1_le\n", + ); + + // scalar_mul cases + let scalars: Vec = vec![ + Fr::from(0u64), + Fr::from(1u64), + Fr::from(2u64), + Fr::from(3u64), + Fr::from(42u64), + Fr::from(0xdeadbeef12345678u64), + Fr::rand(&mut rng), + Fr::rand(&mut rng), + Fr::rand(&mut rng), + ]; + for s in &scalars { + let result = G2Projective::from(gen) + .mul_bigint(s.into_bigint()) + .into_affine(); + let (inf, x0, x1, y0, y1) = format_g2_point(result); + out.push_str(&format!( + "scalar_mul|{}|-|{inf}|{x0}|{x1}|{y0}|{y1}\n", + hex_be(s) + )); + } + + // add cases + let add_pairs: Vec<(Fr, Fr)> = vec![ + (Fr::from(1u64), Fr::from(1u64)), + (Fr::from(3u64), Fr::from(5u64)), + (Fr::rand(&mut rng), Fr::rand(&mut rng)), + ]; + for (s1, s2) in &add_pairs { + let p1 = G2Projective::from(gen).mul_bigint(s1.into_bigint()); + let p2 = G2Projective::from(gen).mul_bigint(s2.into_bigint()); + let result = (p1 + p2).into_affine(); + let (inf, x0, x1, y0, y1) = format_g2_point(result); + out.push_str(&format!( + "add|{}|{}|{inf}|{x0}|{x1}|{y0}|{y1}\n", + hex_be(s1), + hex_be(s2) + )); + } + + // double cases + let double_scalars = [Fr::from(1u64), Fr::from(42u64)]; + for s in &double_scalars { + let p = G2Projective::from(gen).mul_bigint(s.into_bigint()); + let result = p.double().into_affine(); + let (inf, x0, x1, y0, y1) = format_g2_point(result); + out.push_str(&format!( + "double|{}|-|{inf}|{x0}|{x1}|{y0}|{y1}\n", + hex_be(s) + )); + } + + // neg cases + let neg_scalars = [Fr::from(1u64), Fr::from(42u64)]; + for s in &neg_scalars { + let p = G2Projective::from(gen) + .mul_bigint(s.into_bigint()) + .into_affine(); + let result = -p; + let (inf, x0, x1, y0, y1) = format_g2_point(result); + out.push_str(&format!( + "neg|{}|-|{inf}|{x0}|{x1}|{y0}|{y1}\n", + hex_be(s) + )); + } + + write_file(out_dir.join("g2/g2_ops.txt"), out); +} + +fn generate_point_compression(out_dir: &Path) { + let mut rng = StdRng::seed_from_u64(0xc04d_0001); + let g1_gen = G1Affine::generator(); + let g2_gen = G2Affine::generator(); + + // --- G1 --- + let mut g1_out = String::from( + "# name|uncompressed_x_le|uncompressed_y_le|compressed_hex\n", + ); + + let g1_scalars: Vec<(String, Fr)> = vec![ + ("identity".into(), Fr::from(0u64)), + ("generator".into(), Fr::from(1u64)), + ("double".into(), Fr::from(2u64)), + ("scalar_42".into(), Fr::from(42u64)), + ("random_0".into(), Fr::rand(&mut rng)), + ("random_1".into(), Fr::rand(&mut rng)), + ("random_2".into(), Fr::rand(&mut rng)), + ("random_3".into(), Fr::rand(&mut rng)), + ]; + + for (name, s) in &g1_scalars { + let point = G1Projective::from(g1_gen) + .mul_bigint(s.into_bigint()) + .into_affine(); + let mut compressed = Vec::new(); + point.serialize_compressed(&mut compressed).unwrap(); + let (_, x, y) = format_g1_point(point); + g1_out.push_str(&format!( + "{name}|{x}|{y}|{}\n", + hex_encode(&compressed) + )); + } + + write_file(out_dir.join("point_compression/g1_compress.txt"), g1_out); + + // --- G2 --- + let mut g2_out = String::from( + "# name|x_c0_le|x_c1_le|y_c0_le|y_c1_le|compressed_hex\n", + ); + + let g2_scalars: Vec<(String, Fr)> = vec![ + ("identity".into(), Fr::from(0u64)), + ("generator".into(), Fr::from(1u64)), + ("double".into(), Fr::from(2u64)), + ("scalar_42".into(), Fr::from(42u64)), + ("random_0".into(), Fr::rand(&mut rng)), + ("random_1".into(), Fr::rand(&mut rng)), + ("random_2".into(), Fr::rand(&mut rng)), + ("random_3".into(), Fr::rand(&mut rng)), + ]; + + for (name, s) in &g2_scalars { + let point = G2Projective::from(g2_gen) + .mul_bigint(s.into_bigint()) + .into_affine(); + let mut compressed = Vec::new(); + point.serialize_compressed(&mut compressed).unwrap(); + let (_, x0, x1, y0, y1) = format_g2_point(point); + g2_out.push_str(&format!( + "{name}|{x0}|{x1}|{y0}|{y1}|{}\n", + hex_encode(&compressed) + )); + } + + write_file(out_dir.join("point_compression/g2_compress.txt"), g2_out); +} + +fn generate_glv_fixtures(out_dir: &Path) { + let mut rng = StdRng::seed_from_u64(0x614f_0001); + let g1_gen = G1Affine::generator(); + let g2_gen = G2Affine::generator(); + + let scalars: Vec<(String, Fr)> = vec![ + ("zero".into(), Fr::from(0u64)), + ("one".into(), Fr::from(1u64)), + ("two".into(), Fr::from(2u64)), + ("small_42".into(), Fr::from(42u64)), + ("medium".into(), Fr::from(0xdeadbeef12345678u64)), + ("random_0".into(), Fr::rand(&mut rng)), + ("random_1".into(), Fr::rand(&mut rng)), + ("random_2".into(), Fr::rand(&mut rng)), + ("random_3".into(), Fr::rand(&mut rng)), + ("random_4".into(), Fr::rand(&mut rng)), + ]; + + // G1 scalar mul + let mut g1_out = String::from( + "# name|scalar_be_hex|expected_infinity|expected_x_le|expected_y_le\n", + ); + for (name, s) in &scalars { + let result = G1Projective::from(g1_gen) + .mul_bigint(s.into_bigint()) + .into_affine(); + let (inf, x, y) = format_g1_point(result); + g1_out.push_str(&format!("{name}|{}|{inf}|{x}|{y}\n", hex_be(s))); + } + write_file(out_dir.join("glv/glv_g1_scalar_mul.txt"), g1_out); + + // G2 scalar mul + let mut g2_out = String::from( + "# name|scalar_be_hex|expected_infinity|expected_x_c0_le|expected_x_c1_le|expected_y_c0_le|expected_y_c1_le\n", + ); + for (name, s) in &scalars { + let result = G2Projective::from(g2_gen) + .mul_bigint(s.into_bigint()) + .into_affine(); + let (inf, x0, x1, y0, y1) = format_g2_point(result); + g2_out.push_str(&format!( + "{name}|{}|{inf}|{x0}|{x1}|{y0}|{y1}\n", + hex_be(s) + )); + } + write_file(out_dir.join("glv/glv_g2_scalar_mul.txt"), g2_out); +} + +fn generate_dory_commit(out_dir: &Path) { + // Replicate Zolt's exact SRS generation: + // seed = SHA3-256("Jolt Dory URS seed") + // G1[i] = [SHA3-256(seed || le_u64(i) || "G1") as Fr] * G1::generator + // G2[i] = [SHA3-256(seed || le_u64(i+n) || "G2") as Fr] * G2::generator + // h1 = G1::generator, h2 = G2::generator + + let mut hasher = Sha3_256::new(); + hasher.update(b"Jolt Dory URS seed"); + let seed: [u8; 32] = hasher.finalize().into(); + + let g1_gen = G1Affine::generator(); + let g2_gen = G2Affine::generator(); + + let mut out = String::from( + "# name|max_num_vars|evals_be_csv|expected_commitment_fp12_le_hex\n", + ); + + for max_num_vars in [2u64, 3, 4] { + let sigma = (max_num_vars + 1) / 2; + let nu = max_num_vars - sigma; + let num_cols = 1usize << sigma; + let num_rows = 1usize << nu; + let n = std::cmp::max(num_cols, num_rows); + + // Generate SRS points matching Zolt's hash-to-curve + let g1_vec: Vec = (0..n) + .map(|i| { + let mut h = Sha3_256::new(); + h.update(&seed); + h.update(&(i as u64).to_le_bytes()); + h.update(b"G1"); + let hash: [u8; 32] = h.finalize().into(); + let scalar = Fr::from_le_bytes_mod_order(&hash); + G1Projective::from(g1_gen) + .mul_bigint(scalar.into_bigint()) + .into_affine() + }) + .collect(); + + let g2_vec: Vec = (0..n) + .map(|i| { + let mut h = Sha3_256::new(); + h.update(&seed); + h.update(&((i + n) as u64).to_le_bytes()); + h.update(b"G2"); + let hash: [u8; 32] = h.finalize().into(); + let scalar = Fr::from_le_bytes_mod_order(&hash); + G2Projective::from(g2_gen) + .mul_bigint(scalar.into_bigint()) + .into_affine() + }) + .collect(); + + // Known polynomial: [1, 2, ..., 2^max_num_vars] + let poly_len = 1usize << max_num_vars; + let evals: Vec = (1..=poly_len as u64).map(Fr::from).collect(); + + // Compute Dory commitment: for each row, MSM(g1_vec, row_evals), + // then multi-pairing(row_commits, g2_vec), final exponentiation + let mut commitment = Fq12::one(); + for row in 0..num_rows { + let row_start = row * num_cols; + let row_end = std::cmp::min(row_start + num_cols, evals.len()); + let row_evals = &evals[row_start..row_end]; + let row_commit = G1Projective::msm(&g1_vec[..row_evals.len()], row_evals) + .unwrap() + .into_affine(); + if !row_commit.infinity && row < g2_vec.len() { + let pairing_result = Bn254::pairing(row_commit, g2_vec[row]).0; + commitment *= pairing_result; + } + } + + let evals_csv = evals.iter().map(hex_be).collect::>().join(","); + out.push_str(&format!( + "case_nv{max_num_vars}|{max_num_vars}|{evals_csv}|{}\n", + fq12_hex_le(&commitment) + )); + } + + write_file(out_dir.join("dory/commit_cases.txt"), out); +} + +fn generate_gpu_crossover(out_dir: &Path) { + let mut rng = StdRng::seed_from_u64(0x6900_0001); + + let mut out = String::from( + "# op|size|a_be_hex_csv|b_be_hex_csv|expected_be_hex_csv\n", + ); + + let sizes = [4usize, 16, 64]; + for &size in &sizes { + let a: Vec = (0..size).map(|_| Fr::rand(&mut rng)).collect(); + let b: Vec = (0..size).map(|_| Fr::rand(&mut rng)).collect(); + + // mul + let mul_expected: Vec = a.iter().zip(b.iter()).map(|(x, y)| *x * *y).collect(); + out.push_str(&format!( + "mul|{size}|{}|{}|{}\n", + join_field_scalars(&a), + join_field_scalars(&b), + join_field_scalars(&mul_expected) + )); + + // add + let add_expected: Vec = a.iter().zip(b.iter()).map(|(x, y)| *x + *y).collect(); + out.push_str(&format!( + "add|{size}|{}|{}|{}\n", + join_field_scalars(&a), + join_field_scalars(&b), + join_field_scalars(&add_expected) + )); + + // sub + let sub_expected: Vec = a.iter().zip(b.iter()).map(|(x, y)| *x - *y).collect(); + out.push_str(&format!( + "sub|{size}|{}|{}|{}\n", + join_field_scalars(&a), + join_field_scalars(&b), + join_field_scalars(&sub_expected) + )); + + // neg + let neg_expected: Vec = a.iter().map(|x| -*x).collect(); + out.push_str(&format!( + "neg|{size}|{}|-|{}\n", + join_field_scalars(&a), + join_field_scalars(&neg_expected) + )); + } + + write_file(out_dir.join("gpu/field_crossover.txt"), out); +} + +fn parse_out_dir() -> PathBuf { + let mut args = env::args().skip(1); + let mut out_dir = PathBuf::from("testdata/zolt-arith-diff"); + + while let Some(arg) = args.next() { + if arg == "--out-dir" { + out_dir = PathBuf::from(args.next().expect("missing value for --out-dir")); + } else { + panic!("unknown argument: {arg}"); + } + } + + out_dir +} + +fn main() { + let out_dir = parse_out_dir(); + generate_field_ops(&out_dir); + generate_pairing_cases(&out_dir); + generate_msm_cases(&out_dir); + generate_accumulator_ops(&out_dir); + generate_transcript_fixtures(&out_dir); + generate_extension_field_ops(&out_dir); + generate_g2_ops(&out_dir); + generate_point_compression(&out_dir); + generate_glv_fixtures(&out_dir); + generate_dory_commit(&out_dir); + generate_gpu_crossover(&out_dir); + eprintln!("generated zolt-arith differential fixtures under {}", out_dir.display()); +} diff --git a/tools/zolt-arith-diff/arkworks-fixtures/src/transcript.rs b/tools/zolt-arith-diff/arkworks-fixtures/src/transcript.rs new file mode 100644 index 00000000..0b488a0c --- /dev/null +++ b/tools/zolt-arith-diff/arkworks-fixtures/src/transcript.rs @@ -0,0 +1,184 @@ +//! Blake2b-based Fiat-Shamir transcript matching Jolt's Blake2bTranscript. +//! +//! This is an independent Rust reimplementation used as a differential oracle. +//! The protocol is defined in packages/zolt-arith/src/transcripts/blake2b.zig. + +use ark_bn254::Fr; +use ark_ff::{BigInteger, PrimeField}; +use blake2::{Blake2b, Digest}; +use blake2::digest::consts::U32; + +type Blake2b256 = Blake2b; + +/// Blake2b-based Fiat-Shamir transcript matching Jolt (upstream 97b2c96). +pub struct Blake2bTranscript { + state: [u8; 32], + n_rounds: u32, +} + +impl Blake2bTranscript { + pub fn new(label: &[u8]) -> Self { + assert!(label.len() < 33); + let mut padded = [0u8; 32]; + let copy_len = label.len().min(32); + padded[..copy_len].copy_from_slice(&label[..copy_len]); + + let mut h = Blake2b256::new(); + h.update(&padded); + let result = h.finalize(); + let mut state = [0u8; 32]; + state.copy_from_slice(&result); + + Self { state, n_rounds: 0 } + } + + fn hasher(&self) -> Blake2b256 { + let mut h = Blake2b256::new(); + h.update(&self.state); + let mut round_data = [0u8; 32]; + round_data[28..32].copy_from_slice(&self.n_rounds.to_be_bytes()); + h.update(&round_data); + h + } + + fn update_state(&mut self, new_state: [u8; 32]) { + self.state = new_state; + self.n_rounds += 1; + } + + fn finalize_to_state(h: Blake2b256) -> [u8; 32] { + let result = h.finalize(); + let mut state = [0u8; 32]; + state.copy_from_slice(&result); + state + } + + // ===================== Raw methods ===================== + + pub fn raw_append_label(&mut self, label: &[u8]) { + assert!(label.len() < 33); + let mut h = self.hasher(); + if label.len() == 32 { + h.update(label); + } else { + let mut padded = [0u8; 32]; + padded[..label.len()].copy_from_slice(label); + h.update(&padded); + } + let new_state = Self::finalize_to_state(h); + self.update_state(new_state); + } + + fn raw_append_label_with_len(&mut self, label: &[u8], len: u64) { + assert!(label.len() <= 24); + let mut label_buf = [0u8; 32]; + label_buf[..label.len()].copy_from_slice(label); + label_buf[24..32].copy_from_slice(&len.to_be_bytes()); + self.raw_append_bytes(&label_buf); + } + + pub fn raw_append_bytes(&mut self, bytes: &[u8]) { + let mut h = self.hasher(); + h.update(bytes); + let new_state = Self::finalize_to_state(h); + self.update_state(new_state); + } + + pub fn raw_append_u64(&mut self, x: u64) { + let mut data = [0u8; 32]; + data[24..32].copy_from_slice(&x.to_be_bytes()); + let mut h = self.hasher(); + h.update(&data); + let new_state = Self::finalize_to_state(h); + self.update_state(new_state); + } + + pub fn raw_append_scalar(&mut self, scalar: Fr) { + let bigint = scalar.into_bigint(); + let le_bytes = bigint.to_bytes_le(); + let mut reversed = [0u8; 32]; + for i in 0..32 { + reversed[i] = le_bytes[31 - i]; + } + self.raw_append_bytes(&reversed); + } + + // ===================== Public API ===================== + + pub fn append_label(&mut self, label: &[u8]) { + self.raw_append_label(label); + } + + pub fn append_bytes(&mut self, label: &[u8], bytes: &[u8]) { + self.raw_append_label_with_len(label, bytes.len() as u64); + self.raw_append_bytes(bytes); + } + + pub fn append_u64(&mut self, label: &[u8], x: u64) { + self.raw_append_label(label); + self.raw_append_u64(x); + } + + pub fn append_scalar(&mut self, label: &[u8], scalar: Fr) { + self.raw_append_label(label); + self.raw_append_scalar(scalar); + } + + pub fn append_scalars(&mut self, label: &[u8], scalars: &[Fr]) { + self.raw_append_label_with_len(label, scalars.len() as u64); + for scalar in scalars { + self.raw_append_scalar(*scalar); + } + } + + // ===================== Challenge generation ===================== + + fn challenge_bytes_32(&mut self) -> [u8; 32] { + let h = self.hasher(); + let out = Self::finalize_to_state(h); + self.update_state(out); + out + } + + pub fn challenge_bytes(&mut self, len: usize) -> Vec { + let mut out = vec![0u8; len]; + let mut remaining = len; + let mut start = 0; + while remaining > 32 { + let full_rand = self.challenge_bytes_32(); + out[start..start + 32].copy_from_slice(&full_rand); + start += 32; + remaining -= 32; + } + let full_rand = self.challenge_bytes_32(); + out[start..start + remaining].copy_from_slice(&full_rand[..remaining]); + out + } + + pub fn challenge_u128(&mut self) -> u128 { + let buf = self.challenge_bytes(16); + u128::from_le_bytes(buf[..16].try_into().unwrap()) + } + + /// 128-bit challenge masked to 125 bits, stored as [0, 0, low, high] Montgomery limbs. + /// Returns (low, high) u64 pair. + pub fn challenge_scalar_128bits(&mut self) -> (u64, u64) { + let buf = self.challenge_bytes(16); + let full_value = u128::from_le_bytes(buf[..16].try_into().unwrap()); + let mask_125: u128 = (1u128 << 125) - 1; + let masked = full_value & mask_125; + let low = masked as u64; + let high = (masked >> 64) as u64; + (low, high) + } + + // ===================== Accessors ===================== + + pub fn state(&self) -> &[u8; 32] { + &self.state + } + + pub fn n_rounds(&self) -> u32 { + self.n_rounds + } +} diff --git a/tools/zolt-arith-diff/check.zig b/tools/zolt-arith-diff/check.zig new file mode 100644 index 00000000..70a174dc --- /dev/null +++ b/tools/zolt-arith-diff/check.zig @@ -0,0 +1,895 @@ +const std = @import("std"); +const parser = @import("vector_parser.zig"); +const zolt = @import("zolt"); +const diff_config = @import("diff_config"); + +const field = zolt.field; +const pairing = field.pairing; +const msm = zolt.msm; +const glv = msm.glv; +const accumulators = field.accumulators; +const transcripts = zolt.transcripts; +const dory = zolt.poly.commitment.dory; + +const Fr = field.BN254Scalar; +const Fp = field.BN254BaseField; +const Fp2 = pairing.Fp2; +const Fp6 = pairing.Fp6; +const Fp12 = pairing.Fp12; +const G1Affine = msm.AffinePoint(Fp); +const G2Point = pairing.G2Point; +const G1MSM = msm.MSM(Fr, Fp); +const Transcript = transcripts.Blake2bTranscript(Fr); + +fn readFixtureAlloc(allocator: std.mem.Allocator, relative_path: []const u8) ![]u8 { + const full_path = try std.fs.path.join(allocator, &.{ diff_config.fixtures_root, relative_path }); + defer allocator.free(full_path); + return std.fs.cwd().readFileAlloc(allocator, full_path, 16 * 1024 * 1024); +} + +fn fieldToBytesLE(comptime F: type, value: F) [32]u8 { + const standard = value.fromMontgomery(); + var bytes: [32]u8 = undefined; + inline for (0..4) |i| { + std.mem.writeInt(u64, bytes[i * 8 ..][0..8], standard.limbs[i], .little); + } + return bytes; +} + +fn parseFrHex(text: []const u8) !Fr { + const bytes = try parser.parseHexBytesExact(32, text); + return Fr.fromBytesBE(&bytes); +} + +fn parseFpHex(text: []const u8) !Fp { + const bytes = try parser.parseHexBytesExact(32, text); + return Fp.fromBytesBE(&bytes); +} + +fn generateG1Bases(allocator: std.mem.Allocator, n: usize) ![]G1Affine { + var bases = try allocator.alloc(G1Affine, n); + const gen = G1Affine.generator(); + var proj = msm.ProjectivePoint(Fp).fromAffine(gen); + for (0..n) |i| { + bases[i] = proj.toAffine(); + proj = proj.double(); + } + return bases; +} + +fn generateG2Bases(allocator: std.mem.Allocator, n: usize) ![]G2Point { + var bases = try allocator.alloc(G2Point, n); + const gen = G2Point.generator(); + var acc = pairing.G2Projective.fromAffine(gen); + for (0..n) |i| { + bases[i] = acc.toAffine(); + acc = acc.double(); + } + return bases; +} + +fn parseFrCsv(allocator: std.mem.Allocator, text: []const u8) ![]Fr { + return parser.parseCsvExact(Fr, text, allocator, parseFrHex); +} + +fn parseI128Csv(allocator: std.mem.Allocator, text: []const u8) ![]i128 { + return parser.parseCsvExact(i128, text, allocator, struct { + fn parseOne(part: []const u8) !i128 { + return parser.parseDecimal(i128, part); + } + }.parseOne); +} + +test "zolt-arith differential field fixtures" { + const allocator = std.testing.allocator; + const fixture_sets = [_]struct { path: []const u8, field_type: type }{ + .{ .path = "field/fr_ops.txt", .field_type = Fr }, + .{ .path = "field/fp_ops.txt", .field_type = Fp }, + }; + + inline for (fixture_sets) |fixture_set| { + const fixture_text = try readFixtureAlloc(allocator, fixture_set.path); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + + const F = fixture_set.field_type; + const a_bytes = try parser.parseHexBytesExact(32, fields_split[1]); + const expected_bytes = try parser.parseHexBytesExact(32, fields_split[3]); + const a = F.fromBytesBE(&a_bytes); + const expected = F.fromBytesBE(&expected_bytes); + + if (std.mem.eql(u8, fields_split[0], "inv")) { + try std.testing.expect(a.inverse().?.eql(expected)); + continue; + } + + const b_bytes = try parser.parseHexBytesExact(32, fields_split[2]); + const b = F.fromBytesBE(&b_bytes); + if (std.mem.eql(u8, fields_split[0], "add")) { + try std.testing.expect(a.add(b).eql(expected)); + } else if (std.mem.eql(u8, fields_split[0], "sub")) { + try std.testing.expect(a.sub(b).eql(expected)); + } else if (std.mem.eql(u8, fields_split[0], "mul")) { + try std.testing.expect(a.mul(b).eql(expected)); + } else { + return error.UnknownFieldOperation; + } + } + } +} + +test "zolt-arith differential pairing fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "pairing/generator_cases.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + + const g1_scalar = Fr.fromU64(try parser.parseDecimal(u64, fields_split[1])); + const g2_scalar = Fr.fromU64(try parser.parseDecimal(u64, fields_split[2])); + + const g1_affine = G1MSM.scalarMul(pairing.G1PointInFp.generator(), g1_scalar).toAffine(); + const g1 = pairing.G1PointFp{ + .x = g1_affine.x, + .y = g1_affine.y, + .infinity = g1_affine.infinity, + }; + const g2 = G2Point.generator().scalarMul(g2_scalar); + + const expected = try parser.parseHexBytesExact(384, fields_split[3]); + const actual = pairing.pairingFp(g1, g2).toBytes(); + try std.testing.expectEqualSlices(u8, &expected, &actual); + } +} + +test "zolt-arith differential msm g1 fr fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "msm/g1_fr_cases.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(5, line, '|'); + const scalars = try parseFrCsv(allocator, fields_split[1]); + defer allocator.free(scalars); + + const bases = try generateG1Bases(allocator, scalars.len); + defer allocator.free(bases); + + const actual = G1MSM.computeWithPool(bases, scalars, null); + const expected_infinity = try parser.parseDecimal(u8, fields_split[2]); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x = try parser.parseHexBytesExact(32, fields_split[3]); + const expected_y = try parser.parseHexBytesExact(32, fields_split[4]); + const actual_x = fieldToBytesLE(Fp, actual.x); + const actual_y = fieldToBytesLE(Fp, actual.y); + try std.testing.expectEqualSlices(u8, &expected_x, &actual_x); + try std.testing.expectEqualSlices(u8, &expected_y, &actual_y); + } + } +} + +test "zolt-arith differential msm g1 i128 fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "msm/g1_i128_cases.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(5, line, '|'); + const scalars = try parseI128Csv(allocator, fields_split[1]); + defer allocator.free(scalars); + + const bases = try generateG1Bases(allocator, scalars.len); + defer allocator.free(bases); + + const actual = G1MSM.computeI128(bases, scalars, null); + const expected_infinity = try parser.parseDecimal(u8, fields_split[2]); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x = try parser.parseHexBytesExact(32, fields_split[3]); + const expected_y = try parser.parseHexBytesExact(32, fields_split[4]); + const actual_x = fieldToBytesLE(Fp, actual.x); + const actual_y = fieldToBytesLE(Fp, actual.y); + try std.testing.expectEqualSlices(u8, &expected_x, &actual_x); + try std.testing.expectEqualSlices(u8, &expected_y, &actual_y); + } + } +} + +test "zolt-arith differential msm g2 fr fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "msm/g2_fr_cases.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(7, line, '|'); + const scalars = try parseFrCsv(allocator, fields_split[1]); + defer allocator.free(scalars); + + const bases = try generateG2Bases(allocator, scalars.len); + defer allocator.free(bases); + + const actual = zolt.poly.commitment.dory.msmG2Bench(Fr, bases, scalars, null); + const expected_infinity = try parser.parseDecimal(u8, fields_split[2]); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x_c0 = try parser.parseHexBytesExact(32, fields_split[3]); + const expected_x_c1 = try parser.parseHexBytesExact(32, fields_split[4]); + const expected_y_c0 = try parser.parseHexBytesExact(32, fields_split[5]); + const expected_y_c1 = try parser.parseHexBytesExact(32, fields_split[6]); + const actual_x_c0 = fieldToBytesLE(Fp, actual.x.c0); + const actual_x_c1 = fieldToBytesLE(Fp, actual.x.c1); + const actual_y_c0 = fieldToBytesLE(Fp, actual.y.c0); + const actual_y_c1 = fieldToBytesLE(Fp, actual.y.c1); + try std.testing.expectEqualSlices(u8, &expected_x_c0, &actual_x_c0); + try std.testing.expectEqualSlices(u8, &expected_x_c1, &actual_x_c1); + try std.testing.expectEqualSlices(u8, &expected_y_c0, &actual_y_c0); + try std.testing.expectEqualSlices(u8, &expected_y_c1, &actual_y_c1); + } + } +} + +// ============================================================================ +// Accumulator differential tests +// ============================================================================ + +test "zolt-arith differential sum_of_products fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "accumulator/sum_of_products.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(6, line, '|'); + + const a0 = try parseFrHex(fields_split[1]); + const b0 = try parseFrHex(fields_split[2]); + const a1 = try parseFrHex(fields_split[3]); + const b1 = try parseFrHex(fields_split[4]); + const expected = try parseFrHex(fields_split[5]); + + const actual = Fr.sumOfProducts(.{ a0, a1 }, .{ b0, b1 }); + try std.testing.expect(actual.eql(expected)); + } +} + +test "zolt-arith differential batch_inverse fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "accumulator/batch_inverse.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + + const count = try parser.parseDecimal(usize, fields_split[1]); + const inputs = try parseFrCsv(allocator, fields_split[2]); + defer allocator.free(inputs); + const expected_vals = try parseFrCsv(allocator, fields_split[3]); + defer allocator.free(expected_vals); + + try std.testing.expectEqual(count, inputs.len); + try std.testing.expectEqual(count, expected_vals.len); + + const results = try allocator.alloc(Fr, count); + defer allocator.free(results); + try accumulators.BatchOps.batchInverse(results, inputs, allocator); + + for (0..count) |i| { + try std.testing.expect(results[i].eql(expected_vals[i])); + } + } +} + +test "zolt-arith differential mul_u64 fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "accumulator/mul_u64.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + + const field_val = try parseFrHex(fields_split[1]); + const scalar = try parser.parseDecimal(u64, fields_split[2]); + const expected = try parseFrHex(fields_split[3]); + + const actual = accumulators.mulU64(field_val, scalar); + try std.testing.expect(actual.eql(expected)); + } +} + +test "zolt-arith differential mul_u128 fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "accumulator/mul_u128.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + + const field_val = try parseFrHex(fields_split[1]); + const scalar = try parser.parseDecimal(u128, fields_split[2]); + const expected = try parseFrHex(fields_split[3]); + + const unreduced = accumulators.mulU128Unreduced(field_val, scalar); + const actual = accumulators.reduceMulU128(unreduced); + try std.testing.expect(actual.eql(expected)); + } +} + +// ============================================================================ +// Transcript differential tests +// ============================================================================ + +/// Apply a semicolon-separated list of public transcript operations. +/// Format: "append_label:data;append_u64:count:999;append_scalar:val:7" +fn applyTranscriptOps(transcript: *Transcript, ops_desc: []const u8) !void { + if (std.mem.eql(u8, ops_desc, "-")) return; + var ops = std.mem.splitScalar(u8, ops_desc, ';'); + while (ops.next()) |op| { + var parts = std.mem.splitScalar(u8, op, ':'); + const kind = parts.next() orelse continue; + if (std.mem.eql(u8, kind, "append_label")) { + transcript.appendLabel(parts.next() orelse return error.MissingArg); + } else if (std.mem.eql(u8, kind, "append_u64")) { + const label = parts.next() orelse return error.MissingArg; + const val_str = parts.next() orelse return error.MissingArg; + transcript.appendU64(label, try parser.parseDecimal(u64, val_str)); + } else if (std.mem.eql(u8, kind, "append_scalar")) { + const label = parts.next() orelse return error.MissingArg; + const val_str = parts.next() orelse return error.MissingArg; + transcript.appendScalar(label, Fr.fromU64(try parser.parseDecimal(u64, val_str))); + } else { + return error.UnknownTranscriptOp; + } + } +} + +test "zolt-arith differential transcript state fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "transcript/state_vectors.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + // Format: name|init_label|ops_desc|expected_state_hex|expected_rounds + const fields_split = try parser.splitFieldsExact(5, line, '|'); + + const init_label = fields_split[1]; + const ops_desc = fields_split[2]; + const expected_state = try parser.parseHexBytesExact(32, fields_split[3]); + const expected_rounds = try parser.parseDecimal(u32, fields_split[4]); + + var transcript = Transcript.init(init_label); + try applyTranscriptOps(&transcript, ops_desc); + + try std.testing.expectEqualSlices(u8, &expected_state, &transcript.debugState()); + try std.testing.expectEqual(expected_rounds, transcript.n_rounds); + } +} + +test "zolt-arith differential transcript challenge fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "transcript/challenge_vectors.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + // Format: name|init_label|ops_desc|expected_u128|expected_limb2_hex|expected_limb3_hex + const fields_split = try parser.splitFieldsExact(6, line, '|'); + + const init_label = fields_split[1]; + const ops_desc = fields_split[2]; + const expected_u128_str = fields_split[3]; + const expected_low_str = fields_split[4]; + const expected_high_str = fields_split[5]; + + // Check challenge_u128 + if (!std.mem.eql(u8, expected_u128_str, "-")) { + var transcript = Transcript.init(init_label); + try applyTranscriptOps(&transcript, ops_desc); + const expected_u128 = try parser.parseDecimal(u128, expected_u128_str); + try std.testing.expectEqual(expected_u128, transcript.challengeU128()); + } + + // Check challenge_scalar_128bits + if (!std.mem.eql(u8, expected_low_str, "-") and !std.mem.eql(u8, expected_high_str, "-")) { + var transcript = Transcript.init(init_label); + try applyTranscriptOps(&transcript, ops_desc); + const expected_low = try std.fmt.parseInt(u64, expected_low_str, 16); + const expected_high = try std.fmt.parseInt(u64, expected_high_str, 16); + const challenge = transcript.challengeScalar128Bits(); + try std.testing.expectEqual(@as(u64, 0), challenge.limbs[0]); + try std.testing.expectEqual(@as(u64, 0), challenge.limbs[1]); + try std.testing.expectEqual(expected_low, challenge.limbs[2]); + try std.testing.expectEqual(expected_high, challenge.limbs[3]); + } + } +} + +// ============================================================================ +// Extension field differential tests +// ============================================================================ + +fn fpFromBytesLE(bytes: *const [32]u8) Fp { + var limbs: [4]u64 = undefined; + inline for (0..4) |i| { + limbs[i] = std.mem.readInt(u64, bytes[i * 8 ..][0..8], .little); + } + const raw = Fp{ .limbs = limbs }; + return raw.toMontgomery(); +} + +fn parseFp2HexLE(text: []const u8) !Fp2 { + const bytes = try parser.parseHexBytesExact(64, text); + return Fp2.init(fpFromBytesLE(bytes[0..32]), fpFromBytesLE(bytes[32..64])); +} + +fn parseFp6HexLE(text: []const u8) !Fp6 { + const bytes = try parser.parseHexBytesExact(192, text); + return Fp6{ + .c0 = Fp2.init(fpFromBytesLE(bytes[0..32]), fpFromBytesLE(bytes[32..64])), + .c1 = Fp2.init(fpFromBytesLE(bytes[64..96]), fpFromBytesLE(bytes[96..128])), + .c2 = Fp2.init(fpFromBytesLE(bytes[128..160]), fpFromBytesLE(bytes[160..192])), + }; +} + +fn parseFp12HexLE(text: []const u8) !Fp12 { + const bytes = try parser.parseHexBytesExact(384, text); + return Fp12.fromBytes(&bytes); +} + +test "zolt-arith differential fp2 fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "extensions/fp2_ops.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + const op = fields_split[0]; + const a = try parseFp2HexLE(fields_split[1]); + const expected = try parseFp2HexLE(fields_split[3]); + + if (std.mem.eql(u8, op, "add")) { + const b = try parseFp2HexLE(fields_split[2]); + try std.testing.expect(a.add(b).eql(expected)); + } else if (std.mem.eql(u8, op, "sub")) { + const b = try parseFp2HexLE(fields_split[2]); + try std.testing.expect(a.sub(b).eql(expected)); + } else if (std.mem.eql(u8, op, "mul")) { + const b = try parseFp2HexLE(fields_split[2]); + try std.testing.expect(a.mul(b).eql(expected)); + } else if (std.mem.eql(u8, op, "square")) { + try std.testing.expect(a.square().eql(expected)); + } else if (std.mem.eql(u8, op, "inv")) { + try std.testing.expect(a.inverse().?.eql(expected)); + } else if (std.mem.eql(u8, op, "conjugate")) { + try std.testing.expect(a.conjugate().eql(expected)); + } else { + return error.UnknownExtensionFieldOp; + } + case_count += 1; + } + try std.testing.expect(case_count >= 40); +} + +test "zolt-arith differential fp6 fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "extensions/fp6_ops.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + const op = fields_split[0]; + const a = try parseFp6HexLE(fields_split[1]); + const expected = try parseFp6HexLE(fields_split[3]); + + if (std.mem.eql(u8, op, "add")) { + const b = try parseFp6HexLE(fields_split[2]); + try std.testing.expect(a.add(b).eql(expected)); + } else if (std.mem.eql(u8, op, "sub")) { + const b = try parseFp6HexLE(fields_split[2]); + try std.testing.expect(a.sub(b).eql(expected)); + } else if (std.mem.eql(u8, op, "mul")) { + const b = try parseFp6HexLE(fields_split[2]); + try std.testing.expect(a.mul(b).eql(expected)); + } else if (std.mem.eql(u8, op, "square")) { + try std.testing.expect(a.square().eql(expected)); + } else if (std.mem.eql(u8, op, "inv")) { + try std.testing.expect(a.inverse().?.eql(expected)); + } else { + return error.UnknownExtensionFieldOp; + } + case_count += 1; + } + try std.testing.expect(case_count >= 30); +} + +test "zolt-arith differential fp12 fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "extensions/fp12_ops.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + const op = fields_split[0]; + const a = try parseFp12HexLE(fields_split[1]); + const expected = try parseFp12HexLE(fields_split[3]); + + if (std.mem.eql(u8, op, "add")) { + const b = try parseFp12HexLE(fields_split[2]); + try std.testing.expect(a.add(b).eql(expected)); + } else if (std.mem.eql(u8, op, "sub")) { + const b = try parseFp12HexLE(fields_split[2]); + try std.testing.expect(a.sub(b).eql(expected)); + } else if (std.mem.eql(u8, op, "mul")) { + const b = try parseFp12HexLE(fields_split[2]); + try std.testing.expect(a.mul(b).eql(expected)); + } else if (std.mem.eql(u8, op, "square")) { + try std.testing.expect(a.square().eql(expected)); + } else if (std.mem.eql(u8, op, "inv")) { + try std.testing.expect(a.inverse().?.eql(expected)); + } else if (std.mem.eql(u8, op, "conjugate")) { + try std.testing.expect(a.conjugate().eql(expected)); + } else if (std.mem.eql(u8, op, "frobenius")) { + try std.testing.expect(a.frobenius().eql(expected)); + } else if (std.mem.eql(u8, op, "frobenius2")) { + try std.testing.expect(a.frobenius2().eql(expected)); + } else if (std.mem.eql(u8, op, "frobenius3")) { + try std.testing.expect(a.frobenius3().eql(expected)); + } else if (std.mem.eql(u8, op, "cyclotomic_square")) { + try std.testing.expect(a.cyclotomicSquare().eql(expected)); + } else { + return error.UnknownExtensionFieldOp; + } + case_count += 1; + } + try std.testing.expect(case_count >= 60); +} + +// ============================================================================ +// G2 curve ops differential tests +// ============================================================================ + +fn compareG2Coords(actual: G2Point, fields: struct { xc0: []const u8, xc1: []const u8, yc0: []const u8, yc1: []const u8 }) !void { + const expected_x_c0 = try parser.parseHexBytesExact(32, fields.xc0); + const expected_x_c1 = try parser.parseHexBytesExact(32, fields.xc1); + const expected_y_c0 = try parser.parseHexBytesExact(32, fields.yc0); + const expected_y_c1 = try parser.parseHexBytesExact(32, fields.yc1); + const actual_x_c0 = fieldToBytesLE(Fp, actual.x.c0); + const actual_x_c1 = fieldToBytesLE(Fp, actual.x.c1); + const actual_y_c0 = fieldToBytesLE(Fp, actual.y.c0); + const actual_y_c1 = fieldToBytesLE(Fp, actual.y.c1); + try std.testing.expectEqualSlices(u8, &expected_x_c0, &actual_x_c0); + try std.testing.expectEqualSlices(u8, &expected_x_c1, &actual_x_c1); + try std.testing.expectEqualSlices(u8, &expected_y_c0, &actual_y_c0); + try std.testing.expectEqualSlices(u8, &expected_y_c1, &actual_y_c1); +} + +test "zolt-arith differential g2 ops fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "g2/g2_ops.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + const gen = G2Point.generator(); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(8, line, '|'); + const op = fields_split[0]; + const expected_infinity = try parser.parseDecimal(u8, fields_split[3]); + + if (std.mem.eql(u8, op, "scalar_mul")) { + const scalar = try parseFrHex(fields_split[1]); + const actual = gen.scalarMul(scalar); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + try compareG2Coords(actual, .{ .xc0 = fields_split[4], .xc1 = fields_split[5], .yc0 = fields_split[6], .yc1 = fields_split[7] }); + } + } else if (std.mem.eql(u8, op, "add")) { + const s1 = try parseFrHex(fields_split[1]); + const s2 = try parseFrHex(fields_split[2]); + const actual = gen.scalarMul(s1).add(gen.scalarMul(s2)); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + try compareG2Coords(actual, .{ .xc0 = fields_split[4], .xc1 = fields_split[5], .yc0 = fields_split[6], .yc1 = fields_split[7] }); + } + } else if (std.mem.eql(u8, op, "double")) { + const s = try parseFrHex(fields_split[1]); + const p = gen.scalarMul(s); + const actual = p.add(p); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + try compareG2Coords(actual, .{ .xc0 = fields_split[4], .xc1 = fields_split[5], .yc0 = fields_split[6], .yc1 = fields_split[7] }); + } + } else if (std.mem.eql(u8, op, "neg")) { + const s = try parseFrHex(fields_split[1]); + const actual = gen.scalarMul(s).neg(); + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + try compareG2Coords(actual, .{ .xc0 = fields_split[4], .xc1 = fields_split[5], .yc0 = fields_split[6], .yc1 = fields_split[7] }); + } + } else { + return error.UnknownG2Op; + } + case_count += 1; + } + try std.testing.expect(case_count >= 10); +} + +// ============================================================================ +// Point compression differential tests +// ============================================================================ + +test "zolt-arith differential g1 point compression fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "point_compression/g1_compress.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + + const expected_compressed = try parser.parseHexBytesExact(32, fields_split[3]); + + // Reconstruct point from uncompressed coords (LE hex) + // Identity case: x and y are empty strings + if (fields_split[1].len == 0) { + // Identity point + const identity = G1Affine.identity(); + const actual_compressed = dory.compressG1(identity); + try std.testing.expectEqualSlices(u8, &expected_compressed, &actual_compressed); + // Decompress and verify + const decompressed = dory.decompressG1(&actual_compressed); + try std.testing.expect(decompressed != null); + try std.testing.expect(decompressed.?.infinity); + } else { + const x_bytes = try parser.parseHexBytesExact(32, fields_split[1]); + const y_bytes = try parser.parseHexBytesExact(32, fields_split[2]); + const x = fpFromBytesLE(&x_bytes); + const y = fpFromBytesLE(&y_bytes); + const point = G1Affine{ .x = x, .y = y, .infinity = false }; + + // Test compression + const actual_compressed = dory.compressG1(point); + try std.testing.expectEqualSlices(u8, &expected_compressed, &actual_compressed); + + // Test decompression roundtrip + const decompressed = dory.decompressG1(&actual_compressed); + try std.testing.expect(decompressed != null); + try std.testing.expect(!decompressed.?.infinity); + try std.testing.expectEqualSlices(u8, &x_bytes, &fieldToBytesLE(Fp, decompressed.?.x)); + try std.testing.expectEqualSlices(u8, &y_bytes, &fieldToBytesLE(Fp, decompressed.?.y)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 6); +} + +test "zolt-arith differential g2 point compression fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "point_compression/g2_compress.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(6, line, '|'); + + if (fields_split[1].len == 0) { + // Identity: verify compress roundtrip + const identity = G2Point.identity(); + const compressed = dory.compressG2(identity); + const decompressed = dory.decompressG2(&compressed); + try std.testing.expect(decompressed != null); + try std.testing.expect(decompressed.?.infinity); + } else { + const x_c0_bytes = try parser.parseHexBytesExact(32, fields_split[1]); + const x_c1_bytes = try parser.parseHexBytesExact(32, fields_split[2]); + const y_c0_bytes = try parser.parseHexBytesExact(32, fields_split[3]); + const y_c1_bytes = try parser.parseHexBytesExact(32, fields_split[4]); + const point = G2Point{ + .x = Fp2.init(fpFromBytesLE(&x_c0_bytes), fpFromBytesLE(&x_c1_bytes)), + .y = Fp2.init(fpFromBytesLE(&y_c0_bytes), fpFromBytesLE(&y_c1_bytes)), + .infinity = false, + }; + + // Verify full compress → decompress roundtrip + const compressed = dory.compressG2(point); + const decompressed = dory.decompressG2(&compressed); + try std.testing.expect(decompressed != null); + try std.testing.expect(!decompressed.?.infinity); + try std.testing.expectEqualSlices(u8, &x_c0_bytes, &fieldToBytesLE(Fp, decompressed.?.x.c0)); + try std.testing.expectEqualSlices(u8, &x_c1_bytes, &fieldToBytesLE(Fp, decompressed.?.x.c1)); + try std.testing.expectEqualSlices(u8, &y_c0_bytes, &fieldToBytesLE(Fp, decompressed.?.y.c0)); + try std.testing.expectEqualSlices(u8, &y_c1_bytes, &fieldToBytesLE(Fp, decompressed.?.y.c1)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 6); +} + +// ============================================================================ +// GLV scalar multiplication differential tests +// ============================================================================ + +test "zolt-arith differential glv g1 scalar mul fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "glv/glv_g1_scalar_mul.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(5, line, '|'); + + const scalar = try parseFrHex(fields_split[1]); + const expected_infinity = try parser.parseDecimal(u8, fields_split[2]); + + const actual_proj = glv.glvScalarMulG1(G1Affine.generator(), scalar); + const actual = actual_proj.toAffine(); + + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + const expected_x = try parser.parseHexBytesExact(32, fields_split[3]); + const expected_y = try parser.parseHexBytesExact(32, fields_split[4]); + try std.testing.expectEqualSlices(u8, &expected_x, &fieldToBytesLE(Fp, actual.x)); + try std.testing.expectEqualSlices(u8, &expected_y, &fieldToBytesLE(Fp, actual.y)); + } + case_count += 1; + } + try std.testing.expect(case_count >= 8); +} + +test "zolt-arith differential glv g2 scalar mul fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "glv/glv_g2_scalar_mul.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(7, line, '|'); + + const scalar = try parseFrHex(fields_split[1]); + const expected_infinity = try parser.parseDecimal(u8, fields_split[2]); + + const actual_proj = glv.glvScalarMulG2(G2Point.generator(), scalar); + const actual = actual_proj.toAffine(); + + try std.testing.expectEqual(expected_infinity == 1, actual.infinity); + if (!actual.infinity) { + try compareG2Coords(actual, .{ .xc0 = fields_split[3], .xc1 = fields_split[4], .yc0 = fields_split[5], .yc1 = fields_split[6] }); + } + case_count += 1; + } + try std.testing.expect(case_count >= 8); +} + +// ============================================================================ +// Dory commitment differential tests +// ============================================================================ + +test "zolt-arith differential dory commit fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "dory/commit_cases.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + const DoryScheme = dory.DoryCommitmentScheme(Fr); + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(4, line, '|'); + + const max_num_vars = try parser.parseDecimal(usize, fields_split[1]); + const expected_commitment = try parseFp12HexLE(fields_split[3]); + + // Parse evals + const evals = try parseFrCsv(allocator, fields_split[2]); + defer allocator.free(evals); + + // Generate SRS matching the Rust generator + var srs = try DoryScheme.setup(allocator, max_num_vars); + defer srs.deinit(); + + // Compute commitment + const actual = DoryScheme.commitWithPool(&srs, evals, null); + + // Compare Fp12 bytes + const expected_bytes = expected_commitment.toBytes(); + const actual_bytes = actual.toBytes(); + try std.testing.expectEqualSlices(u8, &expected_bytes, &actual_bytes); + case_count += 1; + } + try std.testing.expect(case_count >= 2); +} + +// ============================================================================ +// GPU field crossover differential tests +// ============================================================================ + +test "zolt-arith differential gpu field crossover fixtures" { + const allocator = std.testing.allocator; + const fixture_text = try readFixtureAlloc(allocator, "gpu/field_crossover.txt"); + defer allocator.free(fixture_text); + var lines = std.mem.splitScalar(u8, fixture_text, '\n'); + var case_count: usize = 0; + + while (lines.next()) |raw_line| { + const line = parser.cleanLine(raw_line) orelse continue; + const fields_split = try parser.splitFieldsExact(5, line, '|'); + const op = fields_split[0]; + + // Parse inputs + const a_vals = try parseFrCsv(allocator, fields_split[2]); + defer allocator.free(a_vals); + const expected_vals = try parseFrCsv(allocator, fields_split[4]); + defer allocator.free(expected_vals); + + // Verify CPU path against fixture + if (std.mem.eql(u8, op, "mul")) { + const b_vals = try parseFrCsv(allocator, fields_split[3]); + defer allocator.free(b_vals); + for (a_vals, 0..) |a, i| { + try std.testing.expect(a.mul(b_vals[i]).eql(expected_vals[i])); + } + } else if (std.mem.eql(u8, op, "add")) { + const b_vals = try parseFrCsv(allocator, fields_split[3]); + defer allocator.free(b_vals); + for (a_vals, 0..) |a, i| { + try std.testing.expect(a.add(b_vals[i]).eql(expected_vals[i])); + } + } else if (std.mem.eql(u8, op, "sub")) { + const b_vals = try parseFrCsv(allocator, fields_split[3]); + defer allocator.free(b_vals); + for (a_vals, 0..) |a, i| { + try std.testing.expect(a.sub(b_vals[i]).eql(expected_vals[i])); + } + } else if (std.mem.eql(u8, op, "neg")) { + for (a_vals, 0..) |a, i| { + try std.testing.expect(a.neg().eql(expected_vals[i])); + } + } else { + return error.UnknownGpuOp; + } + case_count += 1; + } + try std.testing.expect(case_count >= 10); +} diff --git a/tools/zolt-arith-diff/run.sh b/tools/zolt-arith-diff/run.sh new file mode 100755 index 00000000..ada56901 --- /dev/null +++ b/tools/zolt-arith-diff/run.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" + +cargo run --release \ + --manifest-path "$ROOT/tools/zolt-arith-diff/arkworks-fixtures/Cargo.toml" \ + -- \ + --out-dir "$ROOT/testdata/zolt-arith-diff" + +zig build test-zolt-arith-diff diff --git a/tools/zolt-arith-diff/vector_parser.zig b/tools/zolt-arith-diff/vector_parser.zig new file mode 100644 index 00000000..3a628030 --- /dev/null +++ b/tools/zolt-arith-diff/vector_parser.zig @@ -0,0 +1,46 @@ +const std = @import("std"); + +pub fn cleanLine(raw_line: []const u8) ?[]const u8 { + const trimmed = std.mem.trim(u8, raw_line, " \t\r"); + if (trimmed.len == 0 or trimmed[0] == '#') return null; + return trimmed; +} + +pub fn splitFieldsExact(comptime field_count: usize, line: []const u8, separator: u8) ![field_count][]const u8 { + var parts = std.mem.splitScalar(u8, line, separator); + var fields: [field_count][]const u8 = undefined; + + for (0..field_count) |i| { + fields[i] = std.mem.trim(u8, parts.next() orelse return error.NotEnoughFields, " \t\r"); + } + + if (parts.next() != null) return error.TooManyFields; + return fields; +} + +pub fn parseDecimal(comptime T: type, text: []const u8) !T { + return std.fmt.parseInt(T, std.mem.trim(u8, text, " \t\r"), 10); +} + +pub fn parseHexBytesExact(comptime byte_len: usize, text: []const u8) ![byte_len]u8 { + const trimmed = std.mem.trim(u8, text, " \t\r"); + if (trimmed.len != byte_len * 2) return error.InvalidHexLength; + + var out: [byte_len]u8 = undefined; + _ = try std.fmt.hexToBytes(&out, trimmed); + return out; +} + +pub fn parseCsvExact(comptime T: type, text: []const u8, allocator: std.mem.Allocator, parse_one: fn ([]const u8) anyerror!T) ![]T { + var parts = std.mem.splitScalar(u8, std.mem.trim(u8, text, " \t\r"), ','); + var values: std.ArrayListUnmanaged(T) = .{}; + errdefer values.deinit(allocator); + + while (parts.next()) |raw_part| { + const part = std.mem.trim(u8, raw_part, " \t\r"); + if (part.len == 0) continue; + try values.append(allocator, try parse_one(part)); + } + + return values.toOwnedSlice(allocator); +}