diff --git a/crates/neo-fold/Cargo.toml b/crates/neo-fold/Cargo.toml index 9d254a61..72016e98 100644 --- a/crates/neo-fold/Cargo.toml +++ b/crates/neo-fold/Cargo.toml @@ -70,6 +70,10 @@ path = "riscv-tests/test_riscv_fibonacci_compiled_full_prove_verify.rs" name = "test_riscv_u64_output_compiled_full_prove_verify" path = "riscv-tests/test_riscv_u64_output_compiled_full_prove_verify.rs" +[[test]] +name = "test_riscv_circuit_l2_transfer_compiled_trace_prove_verify" +path = "riscv-tests/test_riscv_circuit_l2_transfer_compiled_trace_prove_verify.rs" + [[test]] name = "test_riscv_program_crosscheck" path = "riscv-tests/test_riscv_program_crosscheck.rs" diff --git a/crates/neo-fold/riscv-tests/binaries/circuit_l2_transfer_rom.rs b/crates/neo-fold/riscv-tests/binaries/circuit_l2_transfer_rom.rs new file mode 100644 index 00000000..155aa638 --- /dev/null +++ b/crates/neo-fold/riscv-tests/binaries/circuit_l2_transfer_rom.rs @@ -0,0 +1,2141 @@ +// @generated by crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/export_rom_rs.py +// sha256=fe08ff50b727dc045dda44751dbad85df5915bfaf6a06ba91b764fbe88805f45 + +pub const CIRCUIT_L2_TRANSFER_ROM_BASE: u64 = 0u64; +pub const CIRCUIT_L2_TRANSFER_ROM: [u8; 40556] = [ + 0x17, 0x01, 0x00, 0x00, 0x13, 0x01, 0x01, 0x7f, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x51, 0x73, 0x00, 0x00, + 0x00, 0x6f, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x23, 0x22, + 0x91, 0x00, 0x23, 0x20, 0x21, 0x01, 0x83, 0x26, 0x46, 0x00, 0x63, 0x8e, 0x06, 0x04, 0x83, 0x26, 0x86, 0x00, 0x63, + 0x8a, 0x06, 0x04, 0x37, 0xb7, 0x02, 0x00, 0x03, 0x24, 0x07, 0x91, 0xb3, 0x07, 0xb4, 0x00, 0x37, 0x08, 0x02, 0x00, + 0x63, 0x6e, 0xf8, 0x04, 0x03, 0x26, 0x06, 0x00, 0x23, 0x28, 0xf7, 0x90, 0x37, 0xb7, 0x00, 0x00, 0x13, 0x07, 0x07, + 0x91, 0x33, 0x04, 0x87, 0x00, 0x93, 0x04, 0x05, 0x00, 0x13, 0x05, 0x04, 0x00, 0x13, 0x89, 0x05, 0x00, 0x93, 0x05, + 0x06, 0x00, 0x13, 0x86, 0x06, 0x00, 0x97, 0xa0, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xd2, 0x13, 0x85, 0x04, 0x00, 0x93, + 0x05, 0x09, 0x00, 0x6f, 0x00, 0xc0, 0x03, 0xb7, 0xb6, 0x02, 0x00, 0x37, 0xb6, 0x02, 0x00, 0x03, 0xc0, 0x56, 0x91, + 0x03, 0x24, 0x06, 0x91, 0xb3, 0x06, 0xb4, 0x00, 0x37, 0x07, 0x02, 0x00, 0x63, 0x78, 0xd7, 0x00, 0x13, 0x06, 0x10, + 0x00, 0x23, 0x22, 0xc5, 0x00, 0x6f, 0x00, 0xc0, 0x01, 0x23, 0x28, 0xd6, 0x90, 0x37, 0xb6, 0x00, 0x00, 0x13, 0x06, + 0x06, 0x91, 0x33, 0x04, 0x86, 0x00, 0x13, 0x06, 0x00, 0x00, 0x23, 0x22, 0x85, 0x00, 0x23, 0x20, 0xc5, 0x00, 0x23, + 0x24, 0xb5, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x03, 0x24, 0x81, 0x00, 0x83, 0x24, 0x41, 0x00, 0x03, 0x29, 0x01, 0x00, + 0x13, 0x01, 0x01, 0x01, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, 0x11, 0x02, 0x23, 0x24, 0x81, + 0x02, 0x23, 0x22, 0x91, 0x02, 0x13, 0x04, 0x05, 0x00, 0x03, 0x25, 0x05, 0x00, 0x93, 0x84, 0x85, 0x00, 0x93, 0x15, + 0x15, 0x00, 0x63, 0xe4, 0x95, 0x00, 0x93, 0x84, 0x05, 0x00, 0x63, 0xcc, 0x04, 0x04, 0x63, 0x0a, 0x05, 0x00, 0x83, + 0x25, 0x44, 0x00, 0x23, 0x2c, 0xb1, 0x00, 0x23, 0x20, 0xa1, 0x02, 0x13, 0x05, 0x10, 0x00, 0x23, 0x2e, 0xa1, 0x00, + 0x13, 0x05, 0xc1, 0x00, 0x13, 0x06, 0x81, 0x01, 0x93, 0x85, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0xc0, + 0xed, 0x03, 0x25, 0xc1, 0x00, 0x63, 0x1c, 0x05, 0x02, 0x03, 0x25, 0x01, 0x01, 0x23, 0x20, 0x94, 0x00, 0x23, 0x22, + 0xa4, 0x00, 0x83, 0x20, 0xc1, 0x02, 0x03, 0x24, 0x81, 0x02, 0x83, 0x24, 0x41, 0x02, 0x13, 0x01, 0x01, 0x03, 0x67, + 0x80, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, 0xee, 0x97, 0x90, 0x00, 0x00, + 0xe7, 0x80, 0xc0, 0xf3, 0x03, 0x25, 0x01, 0x01, 0x83, 0x25, 0x41, 0x01, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, + 0xee, 0x97, 0x90, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xf2, 0x13, 0x01, 0x01, 0xf2, 0x23, 0x2e, 0x11, 0x0c, 0x23, 0x2c, + 0x81, 0x0c, 0x23, 0x2a, 0x91, 0x0c, 0x23, 0x28, 0x21, 0x0d, 0x23, 0x26, 0x31, 0x0d, 0x23, 0x24, 0x41, 0x0d, 0x23, + 0x22, 0x51, 0x0d, 0x93, 0x08, 0x20, 0x00, 0x83, 0xa2, 0x05, 0x00, 0x03, 0xa3, 0x45, 0x00, 0x83, 0xa3, 0x85, 0x00, + 0x03, 0xae, 0xc5, 0x00, 0x83, 0xae, 0x05, 0x01, 0x03, 0xaf, 0x45, 0x01, 0x83, 0xaf, 0x85, 0x01, 0x83, 0xa5, 0xc5, + 0x01, 0x03, 0x24, 0x07, 0x00, 0x83, 0x24, 0x47, 0x00, 0x03, 0x29, 0x87, 0x00, 0x83, 0x29, 0xc7, 0x00, 0x23, 0x20, + 0x11, 0x01, 0x23, 0x22, 0x01, 0x00, 0x23, 0x24, 0x51, 0x00, 0x23, 0x26, 0x61, 0x00, 0x83, 0x28, 0x07, 0x01, 0x83, + 0x22, 0x47, 0x01, 0x03, 0x23, 0x87, 0x01, 0x03, 0x27, 0xc7, 0x01, 0x23, 0x28, 0x71, 0x00, 0x23, 0x2a, 0xc1, 0x01, + 0x23, 0x2c, 0xd1, 0x01, 0x23, 0x2e, 0xe1, 0x01, 0x83, 0xa3, 0x07, 0x01, 0x03, 0xae, 0x47, 0x01, 0x83, 0xae, 0x87, + 0x01, 0x03, 0xaf, 0xc7, 0x01, 0x23, 0x20, 0xf1, 0x03, 0x23, 0x22, 0xb1, 0x02, 0x23, 0x24, 0xc1, 0x02, 0x23, 0x26, + 0xd1, 0x02, 0x83, 0xa5, 0x07, 0x00, 0x03, 0xa6, 0x47, 0x00, 0x83, 0xa6, 0x87, 0x00, 0x83, 0xa7, 0xc7, 0x00, 0x23, + 0x28, 0x81, 0x02, 0x23, 0x2a, 0x91, 0x02, 0x23, 0x2c, 0x21, 0x03, 0x23, 0x2e, 0x31, 0x03, 0x83, 0x2f, 0x08, 0x00, + 0x03, 0x24, 0x48, 0x00, 0x83, 0x24, 0x88, 0x00, 0x03, 0x29, 0xc8, 0x00, 0x23, 0x20, 0x11, 0x05, 0x23, 0x22, 0x51, + 0x04, 0x23, 0x24, 0x61, 0x04, 0x23, 0x26, 0xe1, 0x04, 0x03, 0x27, 0x08, 0x01, 0x83, 0x28, 0x48, 0x01, 0x83, 0x22, + 0x88, 0x01, 0x03, 0x28, 0xc8, 0x01, 0x23, 0x20, 0x71, 0x06, 0x23, 0x22, 0xc1, 0x07, 0x23, 0x24, 0xd1, 0x07, 0x23, + 0x26, 0xe1, 0x07, 0x37, 0xb3, 0x02, 0x00, 0x23, 0x28, 0xb1, 0x04, 0x23, 0x2a, 0xc1, 0x04, 0x23, 0x2c, 0xd1, 0x04, + 0x23, 0x2e, 0xf1, 0x04, 0xb7, 0xb5, 0x02, 0x00, 0x23, 0x28, 0xf1, 0x07, 0x23, 0x2a, 0x81, 0x06, 0x23, 0x2c, 0x91, + 0x06, 0x23, 0x2e, 0x21, 0x07, 0x23, 0x20, 0xe1, 0x08, 0x23, 0x22, 0x11, 0x09, 0x23, 0x24, 0x51, 0x08, 0x23, 0x26, + 0x01, 0x09, 0x03, 0x40, 0x53, 0x91, 0x83, 0xa6, 0x05, 0x91, 0x37, 0x06, 0x02, 0x00, 0x13, 0x06, 0x16, 0xf7, 0x63, + 0xee, 0xc6, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x86, 0xf0, 0x13, 0x05, 0x10, 0x00, 0x93, 0x05, 0x00, 0x09, + 0x97, 0x90, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xdb, 0x13, 0x04, 0x05, 0x00, 0x13, 0x06, 0x00, 0x00, 0x13, 0x85, 0x06, + 0x09, 0x37, 0xb7, 0x00, 0x00, 0x13, 0x07, 0x07, 0x91, 0x23, 0xa8, 0xa5, 0x90, 0x93, 0x05, 0x00, 0x09, 0x93, 0x04, + 0x01, 0x00, 0x33, 0x05, 0xd7, 0x00, 0x13, 0x09, 0x01, 0x09, 0x23, 0x2a, 0xb1, 0x08, 0x23, 0x2c, 0xa1, 0x08, 0x23, + 0x2e, 0x01, 0x08, 0x93, 0x09, 0x70, 0x00, 0x83, 0x25, 0x41, 0x09, 0x83, 0xaa, 0x04, 0x00, 0x03, 0xaa, 0x44, 0x00, + 0xb3, 0x85, 0xc5, 0x40, 0x63, 0xfa, 0xb9, 0x04, 0xb3, 0x05, 0xc5, 0x00, 0x93, 0xd6, 0x8a, 0x01, 0x13, 0xd7, 0x0a, + 0x01, 0x93, 0xd7, 0x8a, 0x00, 0x13, 0x58, 0x8a, 0x01, 0x93, 0x58, 0x0a, 0x01, 0x23, 0x80, 0x55, 0x01, 0xa3, 0x80, + 0xf5, 0x00, 0x23, 0x81, 0xe5, 0x00, 0xa3, 0x81, 0xd5, 0x00, 0x93, 0x56, 0x8a, 0x00, 0x13, 0x06, 0x86, 0x00, 0x93, + 0x84, 0x84, 0x00, 0x23, 0x82, 0x45, 0x01, 0xa3, 0x82, 0xd5, 0x00, 0x23, 0x83, 0x15, 0x01, 0xa3, 0x83, 0x05, 0x01, + 0x23, 0x2e, 0xc1, 0x08, 0xe3, 0x92, 0x24, 0xfb, 0x6f, 0x00, 0x00, 0x02, 0x13, 0x05, 0x41, 0x09, 0x93, 0x05, 0x06, + 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xd4, 0x03, 0x25, 0x81, 0x09, 0x03, 0x26, 0xc1, 0x09, 0x6f, 0xf0, + 0x9f, 0xf9, 0x83, 0x25, 0x81, 0x09, 0x13, 0x05, 0x01, 0x0a, 0x97, 0x60, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xbb, 0x03, + 0x25, 0x01, 0x0a, 0x83, 0x25, 0x41, 0x0a, 0x03, 0x26, 0x81, 0x0a, 0x83, 0x26, 0xc1, 0x0a, 0x03, 0x27, 0x01, 0x0b, + 0x83, 0x27, 0x41, 0x0b, 0x03, 0x28, 0x81, 0x0b, 0x83, 0x28, 0xc1, 0x0b, 0x23, 0x20, 0xa4, 0x00, 0x23, 0x22, 0xb4, + 0x00, 0x23, 0x24, 0xc4, 0x00, 0x23, 0x26, 0xd4, 0x00, 0x23, 0x28, 0xe4, 0x00, 0x23, 0x2a, 0xf4, 0x00, 0x23, 0x2c, + 0x04, 0x01, 0x23, 0x2e, 0x14, 0x01, 0x83, 0x20, 0xc1, 0x0d, 0x03, 0x24, 0x81, 0x0d, 0x83, 0x24, 0x41, 0x0d, 0x03, + 0x29, 0x01, 0x0d, 0x83, 0x29, 0xc1, 0x0c, 0x03, 0x2a, 0x81, 0x0c, 0x83, 0x2a, 0x41, 0x0c, 0x13, 0x01, 0x01, 0x0e, + 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xf6, 0x23, 0x2e, 0x11, 0x08, 0x23, 0x2c, 0x81, 0x08, 0x23, 0x2a, 0x91, + 0x08, 0x23, 0x28, 0x21, 0x09, 0x23, 0x26, 0x31, 0x09, 0x23, 0x24, 0x41, 0x09, 0x23, 0x22, 0x51, 0x09, 0x23, 0x20, + 0x61, 0x09, 0x23, 0x2e, 0x71, 0x07, 0x23, 0x2c, 0x81, 0x07, 0x23, 0x2a, 0x91, 0x07, 0x23, 0x28, 0xa1, 0x07, 0x23, + 0x26, 0xb1, 0x07, 0x03, 0xaa, 0x05, 0x00, 0x83, 0xa4, 0x45, 0x00, 0x03, 0xab, 0x85, 0x00, 0x83, 0xa9, 0xc5, 0x00, + 0x03, 0xa8, 0x05, 0x01, 0x03, 0xa9, 0x45, 0x01, 0x03, 0xa3, 0x85, 0x01, 0x83, 0xab, 0xc5, 0x01, 0x63, 0x06, 0x07, + 0x46, 0x93, 0x02, 0x00, 0x00, 0x83, 0xaa, 0x06, 0x00, 0x37, 0xbe, 0x02, 0x00, 0xb7, 0xbc, 0x02, 0x00, 0xb7, 0x05, + 0x02, 0x00, 0x13, 0x84, 0x15, 0xfb, 0x03, 0xaf, 0x0a, 0x00, 0x83, 0xa7, 0x4a, 0x00, 0x83, 0xad, 0x8a, 0x00, 0x83, + 0xae, 0xca, 0x00, 0x83, 0xa8, 0x0a, 0x01, 0x83, 0xaf, 0x4a, 0x01, 0x83, 0xa3, 0x8a, 0x01, 0x83, 0xa0, 0xca, 0x01, + 0x93, 0x8a, 0x0a, 0x02, 0x23, 0xa0, 0x56, 0x01, 0x03, 0x40, 0x5e, 0x91, 0x83, 0xa5, 0x0c, 0x91, 0x63, 0xfe, 0x85, + 0x46, 0x23, 0x28, 0x81, 0x02, 0x33, 0xbe, 0x63, 0x00, 0x23, 0x20, 0xc1, 0x05, 0x23, 0x2e, 0x91, 0x00, 0x33, 0x8e, + 0x70, 0x41, 0x33, 0x8c, 0x63, 0x40, 0x23, 0x22, 0x81, 0x05, 0x13, 0x7d, 0x16, 0x00, 0x23, 0x26, 0x71, 0x03, 0xb3, + 0xbb, 0x08, 0x01, 0x33, 0x8c, 0x2f, 0x41, 0x33, 0x84, 0x08, 0x41, 0x23, 0x2e, 0x81, 0x02, 0x23, 0x22, 0x21, 0x03, + 0x13, 0x09, 0x0a, 0x00, 0x33, 0xba, 0x6d, 0x01, 0x23, 0x2c, 0x61, 0x01, 0x23, 0x2a, 0x01, 0x01, 0x33, 0x88, 0x3e, + 0x41, 0x33, 0x84, 0x6d, 0x41, 0x23, 0x26, 0x81, 0x00, 0x23, 0x20, 0x31, 0x03, 0xb3, 0x39, 0x2f, 0x01, 0x03, 0x24, + 0x01, 0x04, 0x33, 0x0e, 0x8e, 0x40, 0x23, 0x24, 0xc1, 0x03, 0x33, 0x84, 0x97, 0x40, 0x33, 0x0e, 0x7c, 0x41, 0x23, + 0x28, 0xc1, 0x01, 0xb3, 0x0b, 0x2f, 0x41, 0x33, 0x0a, 0x48, 0x41, 0x13, 0x8b, 0x05, 0x05, 0x37, 0xbc, 0x00, 0x00, + 0x13, 0x0c, 0x0c, 0x91, 0xb3, 0x05, 0xbc, 0x00, 0x33, 0x04, 0x34, 0x41, 0x93, 0xd9, 0x02, 0x01, 0x23, 0xa8, 0x6c, + 0x91, 0x13, 0xdb, 0x82, 0x00, 0x23, 0x84, 0x55, 0x00, 0xa3, 0x84, 0x65, 0x01, 0x13, 0xdb, 0x82, 0x01, 0x03, 0x28, + 0x41, 0x04, 0x33, 0x0c, 0xa8, 0x03, 0x23, 0x20, 0xe1, 0x04, 0x23, 0x2c, 0xc1, 0x02, 0x23, 0x2a, 0x51, 0x02, 0x03, + 0x26, 0xc1, 0x03, 0xb3, 0x02, 0xa6, 0x03, 0x03, 0x26, 0xc1, 0x00, 0xb3, 0x0c, 0xa6, 0x03, 0xb3, 0x8b, 0xab, 0x03, + 0x23, 0x82, 0x05, 0x00, 0xa3, 0x82, 0x05, 0x00, 0x23, 0x83, 0x05, 0x00, 0xa3, 0x83, 0x05, 0x00, 0x13, 0x06, 0x10, + 0x00, 0x23, 0x80, 0xc5, 0x00, 0xa3, 0x80, 0x05, 0x00, 0x23, 0x81, 0x05, 0x00, 0xa3, 0x81, 0x05, 0x00, 0x23, 0x86, + 0x05, 0x00, 0xa3, 0x86, 0x05, 0x00, 0x23, 0x87, 0x05, 0x00, 0xa3, 0x87, 0x05, 0x00, 0x23, 0x85, 0x35, 0x01, 0xa3, + 0x85, 0x65, 0x01, 0x03, 0x26, 0x81, 0x02, 0xb3, 0x04, 0xa6, 0x03, 0x33, 0xbb, 0x83, 0x01, 0x33, 0x86, 0x83, 0x41, + 0x23, 0x24, 0xc1, 0x02, 0x33, 0x0e, 0x6c, 0x00, 0x23, 0x22, 0xa1, 0x04, 0x23, 0x2e, 0xd1, 0x02, 0x03, 0x25, 0x01, + 0x01, 0xb3, 0x06, 0xa5, 0x03, 0x33, 0xb3, 0x58, 0x00, 0xb3, 0x83, 0x58, 0x40, 0x83, 0x29, 0x41, 0x01, 0xb3, 0x89, + 0x32, 0x01, 0x33, 0x06, 0xaa, 0x03, 0x33, 0x04, 0xa4, 0x03, 0x33, 0xbd, 0x9d, 0x01, 0xb3, 0x8d, 0x9d, 0x41, 0x03, + 0x28, 0x81, 0x01, 0x33, 0x88, 0x0c, 0x01, 0x33, 0x8a, 0x90, 0x40, 0x03, 0x25, 0xc1, 0x02, 0xb3, 0x84, 0xa4, 0x00, + 0xb3, 0x30, 0x7f, 0x01, 0x33, 0x05, 0x7f, 0x41, 0x33, 0x89, 0x2b, 0x01, 0x33, 0x37, 0x8e, 0x01, 0x33, 0x8f, 0xdf, + 0x40, 0xb3, 0xb2, 0x59, 0x00, 0x83, 0x28, 0x41, 0x02, 0xb3, 0x86, 0x16, 0x01, 0xb3, 0x8e, 0xce, 0x40, 0xb3, 0x38, + 0x98, 0x01, 0x83, 0x2f, 0x01, 0x02, 0x33, 0x06, 0xf6, 0x01, 0xb3, 0x87, 0x87, 0x40, 0xb3, 0x3b, 0x79, 0x01, 0x83, + 0x2f, 0xc1, 0x01, 0x33, 0x04, 0xf4, 0x01, 0x93, 0x5f, 0x89, 0x01, 0x23, 0x2e, 0xf1, 0x01, 0x13, 0x5c, 0x09, 0x01, + 0x93, 0x5f, 0x89, 0x00, 0x33, 0x0a, 0x6a, 0x41, 0x23, 0x26, 0x41, 0x03, 0x13, 0x5a, 0x88, 0x01, 0x23, 0x20, 0x41, + 0x03, 0x33, 0x8b, 0xe4, 0x00, 0x93, 0x5c, 0x08, 0x01, 0x33, 0x07, 0x6f, 0x40, 0x23, 0x22, 0xe1, 0x02, 0x13, 0x5a, + 0x88, 0x00, 0x33, 0x83, 0x56, 0x00, 0x13, 0xd7, 0x89, 0x01, 0xb3, 0x84, 0xae, 0x41, 0x13, 0xdd, 0x09, 0x01, 0xb3, + 0x02, 0x16, 0x01, 0x93, 0xd6, 0x89, 0x00, 0xb3, 0x8e, 0x17, 0x40, 0x93, 0x50, 0x8e, 0x01, 0x33, 0x0f, 0x74, 0x01, + 0x13, 0x56, 0x0e, 0x01, 0x23, 0x88, 0x25, 0x01, 0x13, 0x59, 0x8e, 0x00, 0xa3, 0x88, 0xf5, 0x01, 0x93, 0x58, 0x85, + 0x01, 0x23, 0x89, 0x85, 0x01, 0x13, 0x54, 0x05, 0x01, 0x83, 0x27, 0xc1, 0x01, 0xa3, 0x89, 0xf5, 0x00, 0x93, 0x5f, + 0x85, 0x00, 0x23, 0x8c, 0x05, 0x01, 0x13, 0xd8, 0x8d, 0x01, 0xa3, 0x8c, 0x45, 0x01, 0x13, 0xda, 0x0d, 0x01, 0x23, + 0x8d, 0x95, 0x01, 0x93, 0xdb, 0x8d, 0x00, 0x83, 0x27, 0x01, 0x02, 0xa3, 0x8d, 0xf5, 0x00, 0x13, 0xdc, 0x83, 0x01, + 0x23, 0x80, 0x35, 0x03, 0x93, 0xd9, 0x03, 0x01, 0xa3, 0x80, 0xd5, 0x02, 0x93, 0xd7, 0x83, 0x00, 0x23, 0x81, 0xa5, + 0x03, 0x83, 0x26, 0x81, 0x02, 0x93, 0xdc, 0x86, 0x01, 0xa3, 0x81, 0xe5, 0x02, 0x13, 0xdd, 0x06, 0x01, 0x23, 0x84, + 0xc5, 0x03, 0x13, 0xde, 0x86, 0x00, 0xa3, 0x84, 0x25, 0x03, 0x23, 0x85, 0xc5, 0x02, 0xa3, 0x85, 0x15, 0x02, 0x23, + 0x88, 0xa5, 0x02, 0xa3, 0x88, 0xf5, 0x03, 0x23, 0x89, 0x85, 0x02, 0xa3, 0x89, 0x15, 0x03, 0x23, 0x8c, 0xb5, 0x03, + 0xa3, 0x8c, 0x75, 0x03, 0x23, 0x8d, 0x45, 0x03, 0xa3, 0x8d, 0x05, 0x03, 0x23, 0x80, 0x75, 0x04, 0xa3, 0x80, 0xf5, + 0x04, 0x23, 0x81, 0x35, 0x05, 0xa3, 0x81, 0x85, 0x05, 0x23, 0x84, 0xd5, 0x04, 0xa3, 0x84, 0xc5, 0x05, 0x23, 0x85, + 0xa5, 0x05, 0xa3, 0x85, 0x95, 0x05, 0x13, 0x57, 0x8f, 0x01, 0x13, 0x55, 0x0f, 0x01, 0x93, 0x57, 0x8f, 0x00, 0x13, + 0xde, 0x82, 0x01, 0x93, 0xd8, 0x02, 0x01, 0x93, 0xd3, 0x82, 0x00, 0x13, 0x86, 0x02, 0x00, 0x13, 0x59, 0x83, 0x01, + 0x93, 0x5f, 0x03, 0x01, 0x13, 0x54, 0x83, 0x00, 0x13, 0x08, 0x0b, 0x00, 0x13, 0x5b, 0x8b, 0x01, 0x93, 0x59, 0x08, + 0x01, 0x13, 0x5a, 0x88, 0x00, 0x93, 0xdb, 0x8e, 0x01, 0x13, 0xdc, 0x0e, 0x01, 0x93, 0xdc, 0x8e, 0x00, 0x13, 0xdd, + 0x84, 0x01, 0x93, 0xdd, 0x04, 0x01, 0x93, 0xd0, 0x84, 0x00, 0x93, 0x82, 0x04, 0x00, 0x83, 0x26, 0x41, 0x02, 0x93, + 0xd4, 0x86, 0x01, 0x23, 0x24, 0x91, 0x02, 0x23, 0x8a, 0xe5, 0x01, 0x93, 0xd4, 0x06, 0x01, 0xa3, 0x8a, 0xf5, 0x00, + 0x93, 0xd7, 0x86, 0x00, 0x23, 0x8b, 0xa5, 0x00, 0xa3, 0x8b, 0xe5, 0x00, 0x03, 0x25, 0xc1, 0x02, 0x13, 0x5f, 0x85, + 0x01, 0x23, 0x8e, 0xc5, 0x00, 0xa3, 0x8e, 0x75, 0x00, 0x23, 0x8f, 0x15, 0x01, 0xa3, 0x8f, 0xc5, 0x01, 0x13, 0x56, + 0x05, 0x01, 0x23, 0x82, 0x65, 0x02, 0xa3, 0x82, 0x85, 0x02, 0x03, 0x24, 0x01, 0x03, 0x23, 0x83, 0xf5, 0x03, 0xa3, + 0x83, 0x25, 0x03, 0x13, 0x57, 0x85, 0x00, 0x23, 0x86, 0x05, 0x03, 0xa3, 0x86, 0x45, 0x03, 0x23, 0x87, 0x35, 0x03, + 0xa3, 0x87, 0x65, 0x03, 0x23, 0x8a, 0xd5, 0x03, 0xa3, 0x8a, 0x95, 0x03, 0xb7, 0xbc, 0x02, 0x00, 0x23, 0x8b, 0x85, + 0x03, 0xa3, 0x8b, 0x75, 0x03, 0x23, 0x8e, 0x55, 0x02, 0xa3, 0x8e, 0x15, 0x02, 0x23, 0x8f, 0xb5, 0x03, 0xa3, 0x8f, + 0xa5, 0x03, 0x23, 0x82, 0xd5, 0x04, 0xa3, 0x82, 0xf5, 0x04, 0x23, 0x83, 0x95, 0x04, 0x83, 0x26, 0x81, 0x02, 0xa3, + 0x83, 0xd5, 0x04, 0x23, 0x86, 0xa5, 0x04, 0xa3, 0x86, 0xe5, 0x04, 0x23, 0x87, 0xc5, 0x04, 0xa3, 0x87, 0xe5, 0x05, + 0x13, 0x05, 0x81, 0x04, 0x13, 0x06, 0x00, 0x05, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x6d, 0x37, 0xbe, 0x02, + 0x00, 0x83, 0x22, 0x41, 0x03, 0x03, 0x26, 0x81, 0x03, 0x83, 0x26, 0xc1, 0x03, 0x03, 0x27, 0x01, 0x04, 0x03, 0x25, + 0x41, 0x04, 0x03, 0x2a, 0x81, 0x04, 0x83, 0x24, 0xc1, 0x04, 0x03, 0x2b, 0x01, 0x05, 0x83, 0x29, 0x41, 0x05, 0x03, + 0x28, 0x81, 0x05, 0x03, 0x29, 0xc1, 0x05, 0x03, 0x23, 0x01, 0x06, 0x83, 0x2b, 0x41, 0x06, 0x93, 0x82, 0x12, 0x00, + 0x13, 0x56, 0x16, 0x00, 0xe3, 0x1a, 0x57, 0xba, 0x23, 0x20, 0x45, 0x01, 0x23, 0x22, 0x95, 0x00, 0x23, 0x24, 0x65, + 0x01, 0x23, 0x26, 0x35, 0x01, 0x23, 0x28, 0x05, 0x01, 0x23, 0x2a, 0x25, 0x01, 0x23, 0x2c, 0x65, 0x00, 0x23, 0x2e, + 0x75, 0x01, 0x83, 0x20, 0xc1, 0x09, 0x03, 0x24, 0x81, 0x09, 0x83, 0x24, 0x41, 0x09, 0x03, 0x29, 0x01, 0x09, 0x83, + 0x29, 0xc1, 0x08, 0x03, 0x2a, 0x81, 0x08, 0x83, 0x2a, 0x41, 0x08, 0x03, 0x2b, 0x01, 0x08, 0x83, 0x2b, 0xc1, 0x07, + 0x03, 0x2c, 0x81, 0x07, 0x83, 0x2c, 0x41, 0x07, 0x03, 0x2d, 0x01, 0x07, 0x83, 0x2d, 0xc1, 0x06, 0x13, 0x01, 0x01, + 0x0a, 0x67, 0x80, 0x00, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x86, 0xf0, 0x13, 0x05, 0x10, 0x00, 0x93, 0x05, + 0x00, 0x05, 0x97, 0x80, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x75, 0x13, 0x01, 0x01, 0xaa, 0x23, 0x2e, 0x11, 0x54, 0x23, + 0x2c, 0x81, 0x54, 0x23, 0x2a, 0x91, 0x54, 0x23, 0x28, 0x21, 0x55, 0x23, 0x26, 0x31, 0x55, 0x23, 0x24, 0x41, 0x55, + 0x23, 0x22, 0x51, 0x55, 0x23, 0x20, 0x61, 0x55, 0x23, 0x2e, 0x71, 0x53, 0x23, 0x2c, 0x81, 0x53, 0x23, 0x2a, 0x91, + 0x53, 0x23, 0x28, 0xa1, 0x53, 0x23, 0x26, 0xb1, 0x53, 0x13, 0x04, 0x06, 0x00, 0x23, 0x28, 0xb1, 0x00, 0x93, 0x04, + 0x05, 0x00, 0x13, 0x05, 0x01, 0x1d, 0x13, 0x06, 0x00, 0x18, 0x93, 0x05, 0x00, 0x00, 0x97, 0x90, 0x00, 0x00, 0xe7, + 0x80, 0x00, 0x41, 0x03, 0x26, 0x04, 0x00, 0x13, 0x05, 0x01, 0x1e, 0x93, 0x05, 0x01, 0x36, 0x83, 0x26, 0x06, 0x00, + 0x03, 0x27, 0x46, 0x00, 0x83, 0x27, 0x86, 0x00, 0x03, 0x28, 0xc6, 0x00, 0x83, 0x28, 0x06, 0x01, 0x83, 0x22, 0x46, + 0x01, 0x03, 0x23, 0x86, 0x01, 0x83, 0x23, 0xc6, 0x01, 0x23, 0x28, 0xd5, 0xfe, 0x23, 0x2a, 0xe5, 0xfe, 0x23, 0x2c, + 0xf5, 0xfe, 0x23, 0x2e, 0x05, 0xff, 0x23, 0x20, 0x15, 0x01, 0x23, 0x22, 0x55, 0x00, 0x23, 0x24, 0x65, 0x00, 0x23, + 0x26, 0x75, 0x00, 0x13, 0x05, 0x05, 0x02, 0x13, 0x06, 0x06, 0x02, 0xe3, 0x1c, 0xb5, 0xfa, 0x03, 0x25, 0x06, 0x00, + 0x23, 0x24, 0xa1, 0x1a, 0x13, 0x05, 0x86, 0x00, 0x23, 0x26, 0x81, 0x00, 0x23, 0x20, 0xa4, 0x00, 0x03, 0x25, 0x46, + 0x00, 0x23, 0x22, 0xa1, 0x1a, 0x83, 0xa5, 0x44, 0x00, 0x83, 0xa8, 0x84, 0x00, 0x03, 0xa5, 0xc4, 0x00, 0x03, 0x23, + 0x41, 0x1d, 0x03, 0x26, 0x81, 0x1d, 0x83, 0x26, 0xc1, 0x1d, 0x23, 0x26, 0xd1, 0x1c, 0x83, 0x29, 0x41, 0x1f, 0x03, + 0x2f, 0x81, 0x1f, 0x83, 0x22, 0xc1, 0x1f, 0x93, 0x86, 0x04, 0x00, 0x83, 0x24, 0x41, 0x21, 0x03, 0x2e, 0x81, 0x21, + 0x83, 0x2e, 0xc1, 0x21, 0x83, 0x2a, 0x41, 0x23, 0x03, 0x24, 0x81, 0x23, 0x83, 0x27, 0xc1, 0x23, 0x03, 0x2b, 0x41, + 0x25, 0x03, 0x2a, 0x81, 0x25, 0x03, 0x27, 0xc1, 0x25, 0x83, 0x2b, 0x41, 0x27, 0x83, 0x2f, 0x81, 0x27, 0x03, 0x28, + 0xc1, 0x27, 0x03, 0x2c, 0x41, 0x29, 0x03, 0x29, 0x81, 0x29, 0x83, 0x23, 0xc1, 0x29, 0x33, 0x83, 0x65, 0x40, 0x23, + 0x28, 0x61, 0x14, 0x33, 0x83, 0x35, 0x41, 0x23, 0x2a, 0x61, 0x14, 0x33, 0x83, 0x95, 0x40, 0x23, 0x20, 0x61, 0x16, + 0x33, 0x83, 0x55, 0x41, 0x23, 0x24, 0x61, 0x16, 0x33, 0x83, 0x65, 0x41, 0x23, 0x28, 0x61, 0x16, 0x33, 0x83, 0x75, + 0x41, 0x23, 0x2a, 0x61, 0x16, 0x83, 0x29, 0x41, 0x2b, 0x33, 0x83, 0x85, 0x41, 0x23, 0x22, 0x61, 0x16, 0x03, 0x23, + 0x81, 0x2b, 0x83, 0x24, 0xc1, 0x2b, 0xb3, 0x89, 0x35, 0x41, 0x23, 0x2c, 0x31, 0x15, 0x03, 0x2b, 0x41, 0x2d, 0x83, + 0x29, 0x81, 0x2d, 0x83, 0x2a, 0xc1, 0x2d, 0x03, 0x2c, 0x41, 0x2f, 0x33, 0x8b, 0x65, 0x41, 0x23, 0x26, 0x61, 0x15, + 0x83, 0x2b, 0x81, 0x2f, 0x03, 0x2b, 0xc1, 0x2f, 0x33, 0x8c, 0x85, 0x41, 0x23, 0x24, 0x81, 0x15, 0x83, 0x2c, 0x41, + 0x31, 0x03, 0x2c, 0x81, 0x31, 0x03, 0x2d, 0xc1, 0x31, 0x83, 0x2d, 0x41, 0x33, 0xb3, 0x8c, 0x95, 0x41, 0x23, 0x22, + 0x91, 0x11, 0x83, 0x2c, 0x81, 0x33, 0x83, 0x20, 0xc1, 0x33, 0xb3, 0x85, 0xb5, 0x41, 0x23, 0x2c, 0xb1, 0x0e, 0x83, + 0x25, 0xc1, 0x1c, 0xb3, 0x05, 0xb5, 0x40, 0x23, 0x26, 0xb1, 0x0e, 0xb3, 0x05, 0x55, 0x40, 0x23, 0x2e, 0xb1, 0x0e, + 0xb3, 0x05, 0xd5, 0x41, 0x23, 0x26, 0xb1, 0x10, 0xb3, 0x05, 0xf5, 0x40, 0x23, 0x2c, 0xb1, 0x10, 0xb3, 0x05, 0xe5, + 0x40, 0x23, 0x24, 0xb1, 0x12, 0xb3, 0x05, 0x05, 0x41, 0x23, 0x26, 0xb1, 0x12, 0xb3, 0x05, 0x75, 0x40, 0x23, 0x28, + 0xb1, 0x12, 0xb3, 0x05, 0x95, 0x40, 0x23, 0x2a, 0xb1, 0x12, 0xb3, 0x05, 0x55, 0x41, 0x23, 0x2c, 0xb1, 0x12, 0xb3, + 0x05, 0x65, 0x41, 0x23, 0x22, 0xb1, 0x14, 0xb3, 0x05, 0xa5, 0x41, 0x23, 0x2e, 0xb1, 0x0c, 0x33, 0x05, 0x15, 0x40, + 0x23, 0x20, 0xa1, 0x0e, 0x33, 0xb5, 0xc8, 0x00, 0x23, 0x26, 0xa1, 0x0c, 0x33, 0x85, 0xc8, 0x40, 0x23, 0x2a, 0xa1, + 0x0e, 0x33, 0xb5, 0xe8, 0x01, 0x23, 0x2a, 0xa1, 0x0c, 0x33, 0x85, 0xe8, 0x41, 0x23, 0x20, 0xa1, 0x12, 0x33, 0xb5, + 0xc8, 0x01, 0x23, 0x2c, 0xa1, 0x0c, 0x33, 0x85, 0xc8, 0x41, 0x23, 0x20, 0xa1, 0x14, 0x33, 0xb5, 0x88, 0x00, 0x23, + 0x22, 0xa1, 0x0e, 0x33, 0x85, 0x88, 0x40, 0x23, 0x2e, 0xa1, 0x14, 0x33, 0xb5, 0x48, 0x01, 0x23, 0x28, 0xa1, 0x0e, + 0x33, 0x85, 0x48, 0x41, 0x23, 0x2c, 0xa1, 0x16, 0x33, 0xb5, 0xf8, 0x01, 0x23, 0x24, 0xa1, 0x10, 0x33, 0x85, 0xf8, + 0x41, 0x23, 0x20, 0xa1, 0x18, 0x33, 0xb5, 0x28, 0x01, 0x23, 0x28, 0xa1, 0x10, 0x33, 0x85, 0x28, 0x41, 0x23, 0x22, + 0xa1, 0x1c, 0x33, 0xb5, 0x68, 0x00, 0x23, 0x2a, 0xa1, 0x10, 0x33, 0x85, 0x68, 0x40, 0x23, 0x24, 0xa1, 0x1c, 0x33, + 0xb5, 0x38, 0x01, 0x23, 0x22, 0xa1, 0x12, 0x33, 0x85, 0x38, 0x41, 0x23, 0x26, 0xa1, 0x1c, 0x33, 0xb5, 0x78, 0x01, + 0x23, 0x28, 0xa1, 0x0a, 0x33, 0x85, 0x78, 0x41, 0x23, 0x2c, 0xa1, 0x18, 0x33, 0xb5, 0x88, 0x01, 0x23, 0x2c, 0xa1, + 0x0a, 0x33, 0x85, 0x88, 0x41, 0x23, 0x2e, 0xa1, 0x18, 0x33, 0xb5, 0x98, 0x01, 0x23, 0x20, 0xa1, 0x0c, 0x33, 0x85, + 0x98, 0x41, 0x23, 0x20, 0xa1, 0x1a, 0x83, 0x25, 0x41, 0x1e, 0x93, 0x8f, 0x06, 0x00, 0x83, 0xa2, 0x46, 0x01, 0x03, + 0xa5, 0x86, 0x01, 0x83, 0xa6, 0xc6, 0x01, 0x83, 0x24, 0x81, 0x1e, 0x03, 0x26, 0xc1, 0x1e, 0x23, 0x2a, 0xc1, 0x1a, + 0xb3, 0x85, 0xb2, 0x40, 0x23, 0x20, 0xb1, 0x0a, 0x83, 0x25, 0x41, 0x20, 0x03, 0x24, 0x81, 0x20, 0x03, 0x26, 0xc1, + 0x20, 0x23, 0x28, 0xc1, 0x1a, 0x03, 0x26, 0x41, 0x22, 0xb3, 0x85, 0xb2, 0x40, 0x23, 0x24, 0xb1, 0x0a, 0x03, 0x2e, + 0x81, 0x22, 0x83, 0x25, 0xc1, 0x22, 0x23, 0x26, 0xb1, 0x1a, 0xb3, 0x85, 0xc2, 0x40, 0x23, 0x26, 0xb1, 0x0a, 0x83, + 0x25, 0x41, 0x24, 0x83, 0x29, 0x81, 0x24, 0x03, 0x26, 0xc1, 0x24, 0x23, 0x26, 0xc1, 0x18, 0x03, 0x26, 0x41, 0x26, + 0xb3, 0x85, 0xb2, 0x40, 0x23, 0x2a, 0xb1, 0x0a, 0x83, 0x28, 0x81, 0x26, 0x83, 0x2b, 0xc1, 0x26, 0xb3, 0x85, 0xc2, + 0x40, 0x23, 0x24, 0xb1, 0x0c, 0x83, 0x25, 0x41, 0x28, 0x83, 0x2c, 0x81, 0x28, 0x03, 0x26, 0xc1, 0x28, 0x23, 0x22, + 0xc1, 0x18, 0x03, 0x26, 0x41, 0x2a, 0xb3, 0x85, 0xb2, 0x40, 0x23, 0x22, 0xb1, 0x0c, 0x83, 0x2d, 0x81, 0x2a, 0x03, + 0x2c, 0xc1, 0x2a, 0xb3, 0x85, 0xc2, 0x40, 0x23, 0x26, 0xb1, 0x06, 0x83, 0x25, 0x41, 0x2c, 0x83, 0x20, 0x81, 0x2c, + 0x03, 0x2b, 0xc1, 0x2c, 0x03, 0x26, 0x41, 0x2e, 0xb3, 0x85, 0xb2, 0x40, 0x23, 0x2a, 0xb1, 0x06, 0x03, 0x28, 0x81, + 0x2e, 0x83, 0x2a, 0xc1, 0x2e, 0xb3, 0x85, 0xc2, 0x40, 0x23, 0x2c, 0xb1, 0x06, 0x03, 0x26, 0x41, 0x30, 0x03, 0x23, + 0x81, 0x30, 0x03, 0x2a, 0xc1, 0x30, 0x83, 0x23, 0x41, 0x32, 0xb3, 0x85, 0xc2, 0x40, 0x23, 0x20, 0xb1, 0x08, 0x03, + 0x29, 0x81, 0x32, 0x83, 0x2e, 0xc1, 0x32, 0xb3, 0x85, 0x72, 0x40, 0x23, 0x22, 0xb1, 0x08, 0x03, 0xaf, 0x0f, 0x01, + 0x83, 0x23, 0x41, 0x34, 0x03, 0x26, 0x01, 0x1e, 0x03, 0x2d, 0x81, 0x34, 0x83, 0x27, 0xc1, 0x34, 0xb3, 0x85, 0x72, + 0x40, 0x23, 0x28, 0xb1, 0x06, 0x83, 0x25, 0x01, 0x20, 0x33, 0x37, 0xcf, 0x00, 0x23, 0x2e, 0xe1, 0x06, 0x33, 0x06, + 0xcf, 0x40, 0x23, 0x26, 0xc1, 0x08, 0x03, 0x26, 0x01, 0x22, 0x33, 0x37, 0xbf, 0x00, 0x23, 0x24, 0xe1, 0x08, 0xb3, + 0x05, 0xbf, 0x40, 0x23, 0x22, 0xb1, 0x0a, 0x83, 0x25, 0x01, 0x24, 0x33, 0x37, 0xcf, 0x00, 0x23, 0x28, 0xe1, 0x08, + 0x33, 0x06, 0xcf, 0x40, 0x23, 0x28, 0xc1, 0x0c, 0x03, 0x26, 0x01, 0x26, 0x33, 0x37, 0xbf, 0x00, 0x23, 0x2c, 0xe1, + 0x08, 0xb3, 0x05, 0xbf, 0x40, 0x23, 0x20, 0xb1, 0x10, 0x83, 0x25, 0x01, 0x28, 0x33, 0x37, 0xcf, 0x00, 0x23, 0x2e, + 0xe1, 0x08, 0x33, 0x06, 0xcf, 0x40, 0x23, 0x2e, 0xc1, 0x10, 0x03, 0x26, 0x01, 0x2a, 0x33, 0x37, 0xbf, 0x00, 0x23, + 0x24, 0xe1, 0x02, 0xb3, 0x05, 0xbf, 0x40, 0x23, 0x2e, 0xb1, 0x12, 0x83, 0x25, 0x01, 0x2c, 0x33, 0x37, 0xcf, 0x00, + 0x23, 0x28, 0xe1, 0x02, 0x33, 0x06, 0xcf, 0x40, 0x23, 0x2c, 0xc1, 0x1a, 0x03, 0x26, 0x01, 0x2e, 0x33, 0x37, 0xbf, + 0x00, 0x23, 0x2c, 0xe1, 0x02, 0xb3, 0x05, 0xbf, 0x40, 0x23, 0x2e, 0xb1, 0x1a, 0x83, 0x25, 0x01, 0x30, 0x33, 0x37, + 0xcf, 0x00, 0x23, 0x2e, 0xe1, 0x02, 0x33, 0x06, 0xcf, 0x40, 0x23, 0x20, 0xc1, 0x1c, 0x03, 0x26, 0x01, 0x32, 0x03, + 0x27, 0x01, 0x34, 0xb3, 0x32, 0xbf, 0x00, 0x23, 0x22, 0x51, 0x04, 0xb3, 0x05, 0xbf, 0x40, 0x23, 0x24, 0xb1, 0x18, + 0xb3, 0x35, 0xcf, 0x00, 0x23, 0x24, 0xb1, 0x04, 0xb3, 0x05, 0xcf, 0x40, 0x23, 0x28, 0xb1, 0x18, 0xb3, 0x35, 0xef, + 0x00, 0x23, 0x28, 0xb1, 0x04, 0xb3, 0x05, 0xef, 0x40, 0x23, 0x2a, 0xb1, 0x18, 0x83, 0x25, 0x41, 0x1b, 0xb3, 0x85, + 0xb6, 0x40, 0x03, 0x26, 0x01, 0x1b, 0x33, 0x86, 0xc6, 0x40, 0x03, 0x27, 0xc1, 0x1a, 0x33, 0x87, 0xe6, 0x40, 0x83, + 0x22, 0xc1, 0x18, 0xb3, 0x83, 0x56, 0x40, 0xb3, 0x8b, 0x76, 0x41, 0x83, 0x22, 0x41, 0x18, 0xb3, 0x82, 0x56, 0x40, + 0x23, 0x2a, 0x51, 0x00, 0xb3, 0x82, 0x86, 0x41, 0x23, 0x2c, 0x51, 0x00, 0xb3, 0x82, 0x66, 0x41, 0x23, 0x2e, 0x51, + 0x00, 0xb3, 0x82, 0x56, 0x41, 0x23, 0x20, 0x51, 0x02, 0xb3, 0x82, 0x46, 0x41, 0x23, 0x22, 0x51, 0x02, 0xb3, 0x82, + 0xd6, 0x41, 0x23, 0x26, 0x51, 0x02, 0xb3, 0x86, 0xf6, 0x40, 0x23, 0x2a, 0xd1, 0x02, 0x03, 0xaf, 0x0f, 0x00, 0x33, + 0x3b, 0x95, 0x00, 0xb3, 0x02, 0x95, 0x40, 0xb3, 0x3a, 0x85, 0x00, 0x33, 0x04, 0x85, 0x40, 0xb3, 0x3f, 0xc5, 0x01, + 0xb3, 0x06, 0xc5, 0x41, 0x23, 0x2a, 0xd1, 0x08, 0x33, 0x3c, 0x35, 0x01, 0xb3, 0x06, 0x35, 0x41, 0x23, 0x2e, 0xd1, + 0x0a, 0x33, 0x3a, 0x15, 0x01, 0xb3, 0x06, 0x15, 0x41, 0x23, 0x24, 0xd1, 0x0e, 0xb3, 0x36, 0x95, 0x01, 0xb3, 0x07, + 0x95, 0x41, 0x23, 0x26, 0xf1, 0x1a, 0xb3, 0x37, 0xb5, 0x01, 0xb3, 0x08, 0xb5, 0x41, 0x23, 0x28, 0x11, 0x1b, 0x33, + 0x3e, 0x15, 0x00, 0xb3, 0x08, 0x15, 0x40, 0x23, 0x2a, 0x11, 0x1b, 0xb3, 0x38, 0x05, 0x01, 0x33, 0x08, 0x05, 0x41, + 0x23, 0x26, 0x01, 0x17, 0x33, 0x38, 0x65, 0x00, 0x33, 0x03, 0x65, 0x40, 0x23, 0x2e, 0x61, 0x16, 0x83, 0x20, 0x01, + 0x1d, 0xb3, 0x3e, 0x25, 0x01, 0x33, 0x03, 0x25, 0x41, 0x23, 0x22, 0x61, 0x18, 0xb3, 0x39, 0xa5, 0x01, 0x33, 0x05, + 0xa5, 0x41, 0x23, 0x26, 0xa1, 0x18, 0x33, 0x35, 0x1f, 0x00, 0x03, 0x23, 0x01, 0x15, 0xb3, 0x0d, 0xa3, 0x40, 0x03, + 0x25, 0x01, 0x1f, 0x03, 0x23, 0xc1, 0x0e, 0x83, 0x24, 0xc1, 0x0c, 0xb3, 0x0c, 0x93, 0x40, 0x03, 0x23, 0x01, 0x0a, + 0x83, 0x24, 0xc1, 0x07, 0x33, 0x09, 0x93, 0x40, 0x33, 0x83, 0x65, 0x41, 0xb3, 0x35, 0xaf, 0x00, 0x03, 0x2b, 0x41, + 0x15, 0xb3, 0x04, 0xbb, 0x40, 0x83, 0x25, 0x01, 0x21, 0x03, 0x2d, 0xc1, 0x0f, 0x03, 0x2b, 0x41, 0x0d, 0x33, 0x0b, + 0x6d, 0x41, 0x23, 0x20, 0x61, 0x05, 0x03, 0x2b, 0x81, 0x0a, 0x03, 0x2d, 0x81, 0x08, 0x33, 0x0b, 0xab, 0x41, 0x23, + 0x26, 0x61, 0x05, 0x33, 0x06, 0x56, 0x41, 0x23, 0x2a, 0xc1, 0x04, 0x33, 0x36, 0xbf, 0x00, 0x83, 0x2a, 0x01, 0x16, + 0x33, 0x86, 0xca, 0x40, 0x23, 0x2c, 0xc1, 0x04, 0x03, 0x2d, 0x01, 0x23, 0x03, 0x26, 0xc1, 0x10, 0x83, 0x2a, 0x81, + 0x0d, 0x33, 0x06, 0x56, 0x41, 0x23, 0x2e, 0xc1, 0x04, 0x03, 0x26, 0xc1, 0x0a, 0x83, 0x2a, 0x01, 0x09, 0x33, 0x06, + 0x56, 0x41, 0x23, 0x20, 0xc1, 0x06, 0x33, 0x06, 0xf7, 0x41, 0x23, 0x22, 0xc1, 0x06, 0x33, 0x36, 0xaf, 0x01, 0x03, + 0x27, 0x81, 0x16, 0x33, 0x07, 0xc7, 0x40, 0x23, 0x24, 0xe1, 0x06, 0x83, 0x2f, 0x01, 0x25, 0x03, 0x26, 0x81, 0x11, + 0x03, 0x27, 0x41, 0x0e, 0x33, 0x06, 0xe6, 0x40, 0x23, 0x2e, 0xc1, 0x06, 0x03, 0x26, 0x41, 0x0b, 0x03, 0x27, 0x81, + 0x09, 0x33, 0x0b, 0xe6, 0x40, 0x33, 0x86, 0x83, 0x41, 0x23, 0x24, 0xc1, 0x08, 0x33, 0x36, 0xff, 0x01, 0x03, 0x27, + 0x01, 0x17, 0x33, 0x07, 0xc7, 0x40, 0x23, 0x28, 0xe1, 0x08, 0x03, 0x27, 0x01, 0x27, 0x23, 0x20, 0xe1, 0x0a, 0x03, + 0x26, 0x81, 0x12, 0x83, 0x23, 0x01, 0x0f, 0x33, 0x06, 0x76, 0x40, 0x23, 0x2c, 0xc1, 0x08, 0x03, 0x26, 0x81, 0x0c, + 0x83, 0x23, 0xc1, 0x09, 0xb3, 0x0a, 0x76, 0x40, 0x33, 0x86, 0x4b, 0x41, 0x23, 0x2e, 0xc1, 0x08, 0x33, 0x36, 0xef, + 0x00, 0x03, 0x27, 0x41, 0x17, 0x33, 0x07, 0xc7, 0x40, 0x23, 0x24, 0xe1, 0x0a, 0x03, 0x26, 0x01, 0x29, 0x23, 0x24, + 0xc1, 0x0c, 0x03, 0x27, 0xc1, 0x12, 0x83, 0x23, 0x81, 0x10, 0x33, 0x07, 0x77, 0x40, 0x23, 0x26, 0xe1, 0x0a, 0x03, + 0x27, 0x41, 0x0c, 0x83, 0x23, 0x81, 0x02, 0x33, 0x07, 0x77, 0x40, 0x23, 0x2a, 0xe1, 0x0a, 0x03, 0x27, 0x41, 0x01, + 0xb3, 0x06, 0xd7, 0x40, 0x23, 0x22, 0xd1, 0x0c, 0x33, 0x36, 0xcf, 0x00, 0x83, 0x26, 0x41, 0x16, 0xb3, 0x86, 0xc6, + 0x40, 0x23, 0x26, 0xd1, 0x0c, 0x03, 0x26, 0x01, 0x2b, 0x23, 0x26, 0xc1, 0x0e, 0x83, 0x26, 0x01, 0x13, 0x03, 0x27, + 0x01, 0x11, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x2a, 0xd1, 0x0c, 0x83, 0x26, 0xc1, 0x06, 0x03, 0x27, 0x01, 0x03, 0xb3, + 0x86, 0xe6, 0x40, 0x23, 0x2c, 0xd1, 0x0c, 0x83, 0x26, 0x81, 0x01, 0xb3, 0x86, 0xf6, 0x40, 0x23, 0x22, 0xd1, 0x0e, + 0x33, 0x36, 0xcf, 0x00, 0x83, 0x26, 0x81, 0x15, 0xb3, 0x86, 0xc6, 0x40, 0x23, 0x28, 0xd1, 0x0e, 0x03, 0x26, 0x01, + 0x2d, 0x23, 0x28, 0xc1, 0x10, 0x83, 0x26, 0x41, 0x13, 0x03, 0x27, 0x41, 0x11, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x2e, + 0xd1, 0x0e, 0x83, 0x26, 0x41, 0x07, 0x03, 0x27, 0x81, 0x03, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x24, 0xd1, 0x10, 0x83, + 0x26, 0xc1, 0x01, 0xb3, 0x86, 0xc6, 0x41, 0x23, 0x26, 0xd1, 0x10, 0x33, 0x36, 0xcf, 0x00, 0x83, 0x26, 0xc1, 0x14, + 0xb3, 0x86, 0xc6, 0x40, 0x23, 0x2a, 0xd1, 0x10, 0x03, 0x26, 0x01, 0x2f, 0x23, 0x26, 0xc1, 0x12, 0x83, 0x26, 0x81, + 0x13, 0x03, 0x27, 0x41, 0x12, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x2c, 0xd1, 0x10, 0x83, 0x26, 0x81, 0x07, 0x03, 0x27, + 0xc1, 0x03, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x22, 0xd1, 0x12, 0x83, 0x26, 0x01, 0x02, 0xb3, 0x86, 0x16, 0x41, 0x23, + 0x24, 0xd1, 0x12, 0x33, 0x36, 0xcf, 0x00, 0x83, 0x26, 0x81, 0x14, 0x33, 0x86, 0xc6, 0x40, 0x23, 0x28, 0xc1, 0x12, + 0x03, 0x26, 0x01, 0x31, 0x23, 0x24, 0xc1, 0x14, 0x83, 0x26, 0x41, 0x14, 0x03, 0x27, 0x01, 0x0b, 0xb3, 0x86, 0xe6, + 0x40, 0x23, 0x2a, 0xd1, 0x12, 0x83, 0x26, 0x01, 0x08, 0x03, 0x27, 0x41, 0x04, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x2c, + 0xd1, 0x12, 0x83, 0x26, 0x41, 0x02, 0xb3, 0x86, 0x06, 0x41, 0x23, 0x22, 0xd1, 0x14, 0x33, 0x36, 0xcf, 0x00, 0x83, + 0x26, 0x41, 0x10, 0x33, 0x86, 0xc6, 0x40, 0x23, 0x26, 0xc1, 0x14, 0x03, 0x26, 0x01, 0x33, 0x23, 0x20, 0xc1, 0x16, + 0x83, 0x26, 0xc1, 0x0d, 0x03, 0x27, 0x81, 0x0b, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x28, 0xd1, 0x14, 0x83, 0x26, 0x41, + 0x08, 0x03, 0x27, 0x81, 0x04, 0xb3, 0x86, 0xe6, 0x40, 0x23, 0x2a, 0xd1, 0x14, 0x83, 0x26, 0xc1, 0x02, 0xb3, 0x86, + 0xd6, 0x41, 0x23, 0x2c, 0xd1, 0x14, 0x33, 0x36, 0xcf, 0x00, 0x83, 0x26, 0x81, 0x0f, 0x33, 0x86, 0xc6, 0x40, 0x23, + 0x22, 0xc1, 0x16, 0x03, 0x26, 0x01, 0x0e, 0x83, 0x26, 0x01, 0x0c, 0x33, 0x06, 0xd6, 0x40, 0x23, 0x24, 0xc1, 0x16, + 0x03, 0x26, 0x01, 0x07, 0x83, 0x26, 0x01, 0x05, 0x33, 0x06, 0xd6, 0x40, 0x23, 0x28, 0xc1, 0x16, 0x03, 0x26, 0x41, + 0x03, 0x33, 0x06, 0x36, 0x41, 0x23, 0x2a, 0xc1, 0x16, 0x33, 0x06, 0x1f, 0x40, 0x83, 0x26, 0x41, 0x1a, 0xb3, 0x06, + 0xd6, 0x02, 0x83, 0x27, 0x81, 0x1a, 0x33, 0x37, 0xf6, 0x02, 0x33, 0x06, 0xf6, 0x02, 0xb3, 0x87, 0xfd, 0x02, 0xb3, + 0x06, 0xd7, 0x00, 0xb3, 0x86, 0xf6, 0x00, 0x33, 0x07, 0x96, 0x03, 0x03, 0x28, 0x41, 0x0f, 0xb3, 0x37, 0x06, 0x03, + 0x33, 0x87, 0xe7, 0x00, 0x33, 0x06, 0x06, 0x03, 0xb3, 0x86, 0x06, 0x03, 0xb3, 0x07, 0x26, 0x03, 0x83, 0x28, 0xc1, + 0x08, 0x33, 0x38, 0x16, 0x03, 0xb3, 0x07, 0xf8, 0x00, 0xb3, 0x06, 0xd7, 0x00, 0x33, 0x06, 0x16, 0x03, 0x33, 0x07, + 0x66, 0x02, 0x33, 0x38, 0x56, 0x02, 0x33, 0x07, 0xe8, 0x00, 0xb3, 0x86, 0x16, 0x03, 0x33, 0x05, 0xaf, 0x40, 0x33, + 0x06, 0x56, 0x02, 0x33, 0x08, 0x96, 0x02, 0xb3, 0x38, 0xa6, 0x02, 0x33, 0x88, 0x08, 0x01, 0xb3, 0x86, 0xd7, 0x00, + 0x33, 0x06, 0xa6, 0x02, 0x83, 0x27, 0x01, 0x04, 0xb3, 0x07, 0xf6, 0x02, 0x83, 0x23, 0x01, 0x12, 0xb3, 0x38, 0x76, + 0x02, 0xb3, 0x87, 0xf8, 0x00, 0xb3, 0x86, 0x56, 0x02, 0x33, 0x06, 0x76, 0x02, 0x83, 0x28, 0xc1, 0x04, 0xb3, 0x08, + 0x16, 0x03, 0x03, 0x2e, 0x41, 0x0a, 0xb3, 0x32, 0xc6, 0x03, 0xb3, 0x82, 0x12, 0x01, 0xb3, 0x06, 0xd7, 0x00, 0x33, + 0x06, 0xc6, 0x03, 0x03, 0x27, 0x41, 0x05, 0x33, 0x07, 0xe6, 0x02, 0xb3, 0x38, 0x86, 0x02, 0x33, 0x83, 0xe8, 0x00, + 0x33, 0x85, 0xa6, 0x02, 0xb3, 0x06, 0xbf, 0x40, 0xb3, 0x05, 0x86, 0x02, 0x03, 0x26, 0x81, 0x05, 0x33, 0x86, 0xc5, + 0x02, 0x33, 0xb7, 0xd5, 0x02, 0x33, 0x06, 0xc7, 0x00, 0x33, 0x05, 0xa8, 0x00, 0x33, 0x87, 0xd5, 0x02, 0x83, 0x25, + 0xc1, 0x05, 0xb3, 0x05, 0xb7, 0x02, 0x03, 0x29, 0x01, 0x14, 0x33, 0x38, 0x27, 0x03, 0xb3, 0x05, 0xb8, 0x00, 0x33, + 0x05, 0x75, 0x02, 0x33, 0x08, 0x27, 0x03, 0x03, 0x27, 0x01, 0x06, 0x33, 0x07, 0xe8, 0x02, 0x03, 0x2c, 0x01, 0x0d, + 0xb3, 0x38, 0x88, 0x03, 0x33, 0x87, 0xe8, 0x00, 0x33, 0x85, 0xa7, 0x00, 0xb3, 0x07, 0x88, 0x03, 0x03, 0x28, 0x41, + 0x06, 0xb3, 0x88, 0x07, 0x03, 0x83, 0x24, 0x41, 0x09, 0x33, 0xb8, 0x97, 0x02, 0xb3, 0x08, 0x18, 0x01, 0x33, 0x08, + 0xc5, 0x03, 0x33, 0x05, 0xaf, 0x41, 0xb3, 0x83, 0x97, 0x02, 0x13, 0x8d, 0x04, 0x00, 0x83, 0x27, 0x81, 0x06, 0xb3, + 0x87, 0xf3, 0x02, 0x33, 0xbe, 0xa3, 0x02, 0xb3, 0x07, 0xfe, 0x00, 0xb3, 0x82, 0x02, 0x01, 0xb3, 0x83, 0xa3, 0x02, + 0x03, 0x28, 0xc1, 0x07, 0x33, 0x88, 0x03, 0x03, 0x83, 0x29, 0xc1, 0x15, 0x33, 0xbe, 0x33, 0x03, 0x33, 0x08, 0x0e, + 0x01, 0xb3, 0x82, 0x82, 0x02, 0x33, 0x8e, 0x33, 0x03, 0xb3, 0x03, 0x6e, 0x03, 0x03, 0x2b, 0x01, 0x10, 0xb3, 0x3e, + 0x6e, 0x03, 0xb3, 0x83, 0x7e, 0x00, 0xb3, 0x02, 0x53, 0x00, 0x33, 0x03, 0x6e, 0x03, 0x03, 0x2e, 0x81, 0x08, 0x33, + 0x04, 0xc3, 0x03, 0x83, 0x2b, 0xc1, 0x0b, 0x33, 0x3e, 0x73, 0x03, 0x33, 0x04, 0x8e, 0x00, 0xb3, 0x86, 0xd2, 0x02, + 0x33, 0x0e, 0xff, 0x41, 0xb3, 0x02, 0x73, 0x03, 0x03, 0x23, 0x01, 0x09, 0xb3, 0x8f, 0x62, 0x02, 0x33, 0xb3, 0xc2, + 0x03, 0xb3, 0x0f, 0xf3, 0x01, 0x33, 0x06, 0xd6, 0x00, 0xb3, 0x86, 0xc2, 0x03, 0x83, 0x22, 0x81, 0x09, 0xb3, 0x8e, + 0x56, 0x02, 0x03, 0x2a, 0x81, 0x17, 0xb3, 0xb2, 0x46, 0x03, 0xb3, 0x8e, 0xd2, 0x01, 0x33, 0x06, 0x26, 0x03, 0xb3, + 0x82, 0x46, 0x03, 0xb3, 0x86, 0x52, 0x03, 0x83, 0x2a, 0xc1, 0x11, 0x33, 0xb3, 0x52, 0x03, 0xb3, 0x06, 0xd3, 0x00, + 0x33, 0x86, 0xc5, 0x00, 0xb3, 0x82, 0x52, 0x03, 0x83, 0x25, 0xc1, 0x09, 0xb3, 0x85, 0xb2, 0x02, 0x83, 0x24, 0x81, + 0x0e, 0x33, 0xb3, 0x92, 0x02, 0xb3, 0x05, 0xb3, 0x00, 0x23, 0x20, 0xb1, 0x0e, 0x33, 0x06, 0x86, 0x03, 0x83, 0x25, + 0x01, 0x0a, 0xb3, 0x0d, 0xbf, 0x40, 0xb3, 0x82, 0x92, 0x02, 0x93, 0x85, 0x04, 0x00, 0x03, 0x23, 0x81, 0x0a, 0xb3, + 0x80, 0x62, 0x02, 0x33, 0xb3, 0xb2, 0x03, 0xb3, 0x00, 0x13, 0x00, 0x33, 0x06, 0xc7, 0x00, 0x33, 0x87, 0xb2, 0x03, + 0x83, 0x22, 0xc1, 0x0a, 0xb3, 0x0c, 0x57, 0x02, 0x03, 0x23, 0x01, 0x18, 0xb3, 0x32, 0x67, 0x02, 0xb3, 0x82, 0x92, + 0x01, 0x23, 0x2c, 0x51, 0x0e, 0x33, 0x06, 0xa6, 0x03, 0x33, 0x07, 0x67, 0x02, 0x93, 0x0c, 0x03, 0x00, 0x83, 0x22, + 0x41, 0x0b, 0xb3, 0x04, 0x57, 0x02, 0x03, 0x23, 0xc1, 0x13, 0xb3, 0x32, 0x67, 0x02, 0xb3, 0x82, 0x92, 0x00, 0x23, + 0x22, 0x51, 0x10, 0x33, 0x86, 0xc8, 0x00, 0x33, 0x07, 0x67, 0x02, 0x93, 0x04, 0x03, 0x00, 0x83, 0x28, 0x41, 0x0c, + 0xb3, 0x08, 0x17, 0x03, 0x03, 0x23, 0xc1, 0x1a, 0xb3, 0x32, 0x67, 0x02, 0xb3, 0x88, 0x12, 0x01, 0x23, 0x24, 0x11, + 0x1b, 0x33, 0x05, 0xa6, 0x02, 0x03, 0x26, 0x81, 0x0c, 0xb3, 0x02, 0xcf, 0x40, 0x33, 0x07, 0x67, 0x02, 0x03, 0x26, + 0xc1, 0x0c, 0xb3, 0x08, 0xc7, 0x02, 0x13, 0x86, 0x02, 0x00, 0x23, 0x20, 0x51, 0x14, 0xb3, 0x32, 0x57, 0x02, 0xb3, + 0x88, 0x12, 0x01, 0x23, 0x22, 0x11, 0x1b, 0x33, 0x85, 0xa7, 0x00, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0x41, 0x0d, + 0x33, 0x09, 0xc7, 0x02, 0x03, 0x26, 0x41, 0x1c, 0xb3, 0x37, 0xc7, 0x02, 0xb3, 0x87, 0x27, 0x01, 0x23, 0x20, 0xf1, + 0x12, 0x33, 0x05, 0x35, 0x03, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0x81, 0x0d, 0xb3, 0x07, 0xc7, 0x02, 0x03, 0x26, + 0x81, 0x1b, 0xb3, 0x38, 0xc7, 0x02, 0xb3, 0x87, 0xf8, 0x00, 0x23, 0x2e, 0xf1, 0x14, 0x33, 0x05, 0xa8, 0x00, 0x33, + 0x07, 0xc7, 0x02, 0x03, 0x26, 0x41, 0x0e, 0xb3, 0x08, 0xc7, 0x02, 0x03, 0x26, 0x01, 0x1b, 0x33, 0x38, 0xc7, 0x02, + 0x33, 0x08, 0x18, 0x01, 0x23, 0x2a, 0x01, 0x0f, 0x33, 0x05, 0x65, 0x03, 0x83, 0x27, 0xc1, 0x0e, 0xb3, 0x07, 0xff, + 0x40, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0x01, 0x0f, 0x33, 0x03, 0xc7, 0x02, 0x23, 0x26, 0xf1, 0x0e, 0x33, 0x38, + 0xf7, 0x02, 0x33, 0x08, 0x68, 0x00, 0x23, 0x20, 0x01, 0x11, 0x33, 0x85, 0xa3, 0x00, 0x33, 0x07, 0xf7, 0x02, 0x03, + 0x26, 0xc1, 0x0f, 0x33, 0x08, 0xc7, 0x02, 0x03, 0x26, 0x81, 0x1c, 0xb3, 0x33, 0xc7, 0x02, 0x33, 0x88, 0x03, 0x01, + 0x23, 0x2e, 0x01, 0x0f, 0x33, 0x05, 0x75, 0x03, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0x81, 0x10, 0xb3, 0x03, 0xc7, + 0x02, 0x03, 0x26, 0xc1, 0x1b, 0xb3, 0x39, 0xc7, 0x02, 0xb3, 0x83, 0x79, 0x00, 0x23, 0x24, 0x71, 0x10, 0x33, 0x05, + 0xa4, 0x00, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0xc1, 0x10, 0x33, 0x04, 0xc7, 0x02, 0x03, 0x26, 0x41, 0x1b, 0xb3, + 0x39, 0xc7, 0x02, 0x33, 0x84, 0x89, 0x00, 0x23, 0x26, 0x81, 0x10, 0x33, 0x05, 0xc5, 0x03, 0x83, 0x27, 0x01, 0x11, + 0x33, 0x0e, 0xff, 0x40, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0x41, 0x11, 0x33, 0x0c, 0xc7, 0x02, 0xb3, 0x39, 0xc7, + 0x03, 0xb3, 0x89, 0x89, 0x01, 0x23, 0x2a, 0x31, 0x11, 0x33, 0x85, 0xaf, 0x00, 0x33, 0x07, 0xc7, 0x03, 0x03, 0x26, + 0x81, 0x11, 0xb3, 0x0f, 0xc7, 0x02, 0x03, 0x26, 0xc1, 0x1c, 0xb3, 0x39, 0xc7, 0x02, 0xb3, 0x8f, 0xf9, 0x01, 0x23, + 0x2c, 0xf1, 0x11, 0x33, 0x05, 0x45, 0x03, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0x41, 0x12, 0x33, 0x0b, 0xc7, 0x02, + 0x03, 0x26, 0x01, 0x1c, 0xb3, 0x39, 0xc7, 0x02, 0xb3, 0x89, 0x69, 0x01, 0x23, 0x2c, 0x31, 0x17, 0x33, 0x85, 0xae, + 0x00, 0x33, 0x07, 0xc7, 0x02, 0x03, 0x26, 0x81, 0x12, 0xb3, 0x09, 0xc7, 0x02, 0x03, 0x2c, 0xc1, 0x16, 0xb3, 0x3e, + 0x87, 0x03, 0xb3, 0x8e, 0x3e, 0x01, 0x23, 0x24, 0xd1, 0x13, 0x33, 0x05, 0x55, 0x03, 0x03, 0x26, 0xc1, 0x12, 0xb3, + 0x0b, 0xcf, 0x40, 0x33, 0x07, 0x87, 0x03, 0x03, 0x26, 0x01, 0x13, 0xb3, 0x0e, 0xc7, 0x02, 0x33, 0x3a, 0x77, 0x03, + 0xb3, 0x0e, 0xda, 0x01, 0x23, 0x28, 0xd1, 0x13, 0x33, 0x85, 0xa6, 0x00, 0xb3, 0x06, 0x77, 0x03, 0x03, 0x26, 0x41, + 0x13, 0x33, 0x8d, 0xc6, 0x02, 0x03, 0x2b, 0x81, 0x19, 0x33, 0xb7, 0x66, 0x03, 0x33, 0x0d, 0xa7, 0x01, 0x33, 0x05, + 0xb5, 0x02, 0xb3, 0x86, 0x66, 0x03, 0x83, 0x25, 0x81, 0x13, 0xb3, 0x8a, 0xb6, 0x02, 0x03, 0x24, 0x81, 0x18, 0x33, + 0xb7, 0x86, 0x02, 0xb3, 0x0a, 0x57, 0x01, 0x83, 0x25, 0x01, 0x0e, 0x33, 0x85, 0xa5, 0x00, 0xb3, 0x85, 0x86, 0x02, + 0x03, 0x26, 0x41, 0x14, 0x33, 0x87, 0xc5, 0x02, 0x83, 0x2f, 0xc1, 0x17, 0xb3, 0xb6, 0xf5, 0x03, 0xb3, 0x89, 0xe6, + 0x00, 0x33, 0x05, 0xb5, 0x03, 0x03, 0x26, 0x81, 0x14, 0xb3, 0x02, 0xcf, 0x40, 0xb3, 0x85, 0xf5, 0x03, 0x03, 0x26, + 0xc1, 0x14, 0xb3, 0x8d, 0xc5, 0x02, 0xb3, 0xb6, 0x55, 0x02, 0xb3, 0x8d, 0xb6, 0x01, 0x33, 0x85, 0xa0, 0x00, 0xb3, + 0x85, 0x55, 0x02, 0x03, 0x26, 0x01, 0x15, 0xb3, 0x86, 0xc5, 0x02, 0x83, 0x23, 0xc1, 0x19, 0x33, 0xba, 0x75, 0x02, + 0xb3, 0x0e, 0xda, 0x00, 0x33, 0x05, 0x95, 0x03, 0xb3, 0x85, 0x75, 0x02, 0x03, 0x26, 0x41, 0x15, 0xb3, 0x80, 0xc5, + 0x02, 0x03, 0x23, 0x01, 0x19, 0x33, 0xba, 0x65, 0x02, 0xb3, 0x00, 0x1a, 0x00, 0x03, 0x26, 0x81, 0x0f, 0x33, 0x05, + 0xa6, 0x00, 0xb3, 0x85, 0x65, 0x02, 0x03, 0x26, 0x81, 0x15, 0xb3, 0x8c, 0xc5, 0x02, 0x03, 0x28, 0x41, 0x18, 0x33, + 0xba, 0x05, 0x03, 0xb3, 0x0c, 0x9a, 0x01, 0x33, 0x0a, 0x95, 0x02, 0x23, 0x20, 0xe1, 0x19, 0x03, 0x25, 0x01, 0x16, + 0xb3, 0x06, 0xaf, 0x40, 0x33, 0x85, 0x05, 0x03, 0x83, 0x25, 0x41, 0x16, 0x33, 0x06, 0xb5, 0x02, 0xb3, 0x35, 0xd5, + 0x02, 0xb3, 0x85, 0xc5, 0x00, 0x03, 0x26, 0x41, 0x10, 0x33, 0x0a, 0x46, 0x01, 0x33, 0x05, 0xd5, 0x02, 0x03, 0x26, + 0x81, 0x16, 0xb3, 0x04, 0xc5, 0x02, 0x03, 0x2f, 0x01, 0x1a, 0x33, 0x36, 0xe5, 0x03, 0xb3, 0x04, 0x96, 0x00, 0x03, + 0x26, 0xc1, 0x1a, 0x33, 0x06, 0xca, 0x02, 0x33, 0x05, 0xe5, 0x03, 0x03, 0x27, 0x01, 0x17, 0x33, 0x0a, 0xe5, 0x02, + 0x83, 0x27, 0x41, 0x19, 0x33, 0x39, 0xf5, 0x02, 0x33, 0x09, 0x49, 0x01, 0x03, 0x27, 0x81, 0x1a, 0x33, 0x06, 0xc7, + 0x00, 0x33, 0x05, 0xf5, 0x02, 0x03, 0x27, 0x41, 0x17, 0x33, 0x0a, 0xe5, 0x02, 0x03, 0x27, 0xc1, 0x18, 0xb3, 0x38, + 0xe5, 0x02, 0xb3, 0x88, 0x48, 0x01, 0x03, 0x2a, 0x01, 0x14, 0x33, 0x06, 0x46, 0x03, 0x03, 0x2a, 0x41, 0x1a, 0x33, + 0x06, 0xca, 0x00, 0x03, 0x2a, 0x41, 0x1c, 0x33, 0x06, 0x46, 0x03, 0x03, 0x2a, 0x01, 0x12, 0x33, 0x06, 0xca, 0x00, + 0x03, 0x2a, 0x81, 0x1b, 0x33, 0x06, 0x46, 0x03, 0x03, 0x2a, 0xc1, 0x15, 0x33, 0x06, 0xca, 0x00, 0x03, 0x2a, 0x01, + 0x1b, 0x33, 0x06, 0x46, 0x03, 0x03, 0x2a, 0x41, 0x0f, 0x33, 0x06, 0xca, 0x00, 0x03, 0x2a, 0xc1, 0x0e, 0x33, 0x06, + 0x46, 0x03, 0x03, 0x2a, 0x01, 0x10, 0x33, 0x06, 0xca, 0x00, 0x03, 0x2a, 0x81, 0x1c, 0x33, 0x06, 0x46, 0x03, 0x03, + 0x2a, 0xc1, 0x0f, 0x33, 0x06, 0xca, 0x00, 0x03, 0x2a, 0xc1, 0x1b, 0x33, 0x06, 0x46, 0x03, 0x03, 0x2a, 0x81, 0x10, + 0x33, 0x06, 0xca, 0x00, 0x03, 0x2a, 0x41, 0x1b, 0x33, 0x06, 0x46, 0x03, 0x03, 0x2a, 0xc1, 0x10, 0x33, 0x06, 0xca, + 0x00, 0x33, 0x06, 0xc6, 0x03, 0x03, 0x2e, 0x41, 0x11, 0x33, 0x06, 0xce, 0x00, 0x03, 0x2e, 0xc1, 0x1c, 0x33, 0x06, + 0xc6, 0x03, 0x03, 0x2e, 0x81, 0x11, 0x33, 0x06, 0xce, 0x00, 0x03, 0x2e, 0x01, 0x1c, 0x33, 0x06, 0xc6, 0x03, 0x03, + 0x2e, 0x81, 0x17, 0x33, 0x06, 0xce, 0x00, 0x33, 0x06, 0x86, 0x03, 0x03, 0x2e, 0x81, 0x12, 0x33, 0x06, 0xce, 0x00, + 0x33, 0x06, 0x76, 0x03, 0x03, 0x2e, 0x01, 0x13, 0x33, 0x06, 0xce, 0x00, 0x33, 0x06, 0x66, 0x03, 0x33, 0x06, 0xcd, + 0x00, 0x33, 0x06, 0x86, 0x02, 0x33, 0x86, 0xca, 0x00, 0x33, 0x06, 0xf6, 0x03, 0x33, 0x86, 0xc9, 0x00, 0x33, 0x06, + 0x56, 0x02, 0x33, 0x86, 0xcd, 0x00, 0x33, 0x06, 0x76, 0x02, 0x33, 0x86, 0xce, 0x00, 0x33, 0x06, 0x66, 0x02, 0x33, + 0x86, 0xc0, 0x00, 0x33, 0x06, 0x06, 0x03, 0x33, 0x86, 0xcc, 0x00, 0x33, 0x06, 0xd6, 0x02, 0xb3, 0x85, 0xc5, 0x00, + 0xb3, 0x85, 0xe5, 0x03, 0xb3, 0x85, 0xb4, 0x00, 0xb3, 0x85, 0xf5, 0x02, 0xb3, 0x05, 0xb9, 0x00, 0x33, 0x05, 0xe5, + 0x02, 0xb3, 0x85, 0xe5, 0x02, 0xb3, 0x85, 0xb8, 0x00, 0x13, 0x45, 0x15, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x1c, + 0x05, 0x56, 0x93, 0x05, 0x01, 0x1f, 0x93, 0x06, 0x01, 0x23, 0x93, 0x0a, 0x70, 0x00, 0x03, 0x2e, 0x01, 0x1d, 0x83, + 0x2e, 0x41, 0x1d, 0x03, 0x23, 0x81, 0x1d, 0x83, 0x23, 0xc1, 0x1d, 0x03, 0x24, 0x01, 0x1e, 0x83, 0x24, 0x41, 0x1e, + 0x03, 0x2f, 0x81, 0x1e, 0x03, 0x26, 0xc1, 0x1e, 0x13, 0x05, 0x01, 0x21, 0x83, 0x29, 0x05, 0x01, 0x83, 0x2f, 0x45, + 0x01, 0x03, 0x27, 0x85, 0x01, 0x83, 0x27, 0xc5, 0x01, 0x23, 0x26, 0xf1, 0x1c, 0x83, 0x22, 0x05, 0x00, 0x83, 0x28, + 0x45, 0x00, 0x83, 0x2b, 0x85, 0x00, 0x03, 0x29, 0xc5, 0x00, 0x13, 0x05, 0x01, 0x25, 0x03, 0x2b, 0x05, 0x01, 0x03, + 0x2c, 0x45, 0x01, 0x03, 0x28, 0x85, 0x01, 0x83, 0x27, 0xc5, 0x01, 0x23, 0x28, 0x51, 0x37, 0x23, 0x2a, 0x01, 0x36, + 0x23, 0x2c, 0xc1, 0x37, 0x23, 0x2e, 0xd1, 0x37, 0x83, 0x2e, 0x05, 0x00, 0x03, 0x2e, 0x45, 0x00, 0x03, 0x2a, 0x85, + 0x00, 0x83, 0x2d, 0xc5, 0x00, 0x23, 0x20, 0x61, 0x38, 0x23, 0x22, 0x71, 0x38, 0x23, 0x24, 0x81, 0x38, 0x23, 0x26, + 0x91, 0x38, 0x13, 0x05, 0x01, 0x29, 0x03, 0x2d, 0x05, 0x01, 0x83, 0x2c, 0x45, 0x01, 0x83, 0x23, 0x85, 0x01, 0x03, + 0x23, 0xc5, 0x01, 0x23, 0x28, 0xe1, 0x39, 0x23, 0x20, 0x71, 0x3d, 0x23, 0x22, 0x21, 0x3d, 0x83, 0x24, 0x05, 0x00, + 0x03, 0x24, 0x45, 0x00, 0x83, 0x2b, 0x85, 0x00, 0x83, 0x20, 0xc5, 0x00, 0x23, 0x24, 0x31, 0x3d, 0x23, 0x26, 0xf1, + 0x3d, 0x23, 0x20, 0x41, 0x41, 0x23, 0x22, 0xb1, 0x41, 0x13, 0x05, 0x01, 0x2d, 0x83, 0x2d, 0x05, 0x01, 0x03, 0x29, + 0x45, 0x01, 0x83, 0x2f, 0x85, 0x01, 0x03, 0x2f, 0xc5, 0x01, 0x23, 0x24, 0x61, 0x41, 0x23, 0x26, 0x81, 0x41, 0x23, + 0x20, 0x71, 0x45, 0x03, 0x2c, 0x05, 0x00, 0x83, 0x29, 0x45, 0x00, 0x03, 0x2a, 0x85, 0x00, 0x03, 0x2b, 0xc5, 0x00, + 0x23, 0x22, 0x11, 0x44, 0x23, 0x24, 0xa1, 0x45, 0x23, 0x26, 0x91, 0x45, 0x23, 0x20, 0x41, 0x49, 0x13, 0x05, 0x01, + 0x31, 0x83, 0x2c, 0x05, 0x00, 0x83, 0x2b, 0x45, 0x00, 0x03, 0x2a, 0x85, 0x00, 0x03, 0x2d, 0xc5, 0x00, 0x23, 0x22, + 0x61, 0x49, 0x23, 0x24, 0xb1, 0x49, 0x23, 0x26, 0x21, 0x49, 0x83, 0x2d, 0x05, 0x01, 0x83, 0x20, 0x45, 0x01, 0x03, + 0x29, 0x85, 0x01, 0x03, 0x2b, 0xc5, 0x01, 0x23, 0x20, 0x41, 0x4d, 0x23, 0x22, 0xa1, 0x4d, 0x23, 0x24, 0xb1, 0x4d, + 0x23, 0x26, 0x11, 0x4c, 0x03, 0xaa, 0x05, 0x00, 0x03, 0xad, 0x45, 0x00, 0x83, 0xad, 0x85, 0x00, 0x83, 0xa0, 0xc5, + 0x00, 0x23, 0x2a, 0xc1, 0x38, 0x13, 0x06, 0x01, 0x27, 0x23, 0x2c, 0x41, 0x39, 0x23, 0x2e, 0xa1, 0x39, 0x23, 0x20, + 0xb1, 0x3b, 0x03, 0xaa, 0x05, 0x01, 0x03, 0xad, 0x45, 0x01, 0x83, 0xad, 0x85, 0x01, 0x03, 0xa5, 0xc5, 0x01, 0x23, + 0x22, 0x11, 0x3a, 0x93, 0x00, 0x01, 0x2b, 0x23, 0x24, 0x41, 0x3b, 0x23, 0x26, 0xa1, 0x3b, 0x93, 0x05, 0x01, 0x2f, + 0x23, 0x28, 0xb1, 0x3b, 0x23, 0x2a, 0xa1, 0x3a, 0x23, 0x2c, 0x51, 0x3a, 0x23, 0x2e, 0x11, 0x3b, 0x03, 0xa5, 0x06, + 0x00, 0x83, 0xa8, 0x46, 0x00, 0x83, 0xa2, 0x86, 0x00, 0x03, 0xaa, 0xc6, 0x00, 0x23, 0x28, 0xe1, 0x3c, 0x03, 0x27, + 0xc1, 0x1c, 0x23, 0x2a, 0xe1, 0x3c, 0x23, 0x2c, 0xa1, 0x3c, 0x23, 0x2e, 0x11, 0x3d, 0x03, 0xa5, 0x06, 0x01, 0x03, + 0xa7, 0x46, 0x01, 0x83, 0xa8, 0x86, 0x01, 0x83, 0xa6, 0xc6, 0x01, 0x23, 0x20, 0x51, 0x3e, 0x23, 0x22, 0x41, 0x3f, + 0x23, 0x24, 0xa1, 0x3e, 0x23, 0x26, 0xe1, 0x3e, 0x03, 0x27, 0x06, 0x00, 0x83, 0x22, 0x46, 0x00, 0x03, 0x25, 0x86, + 0x00, 0x03, 0x2a, 0xc6, 0x00, 0x23, 0x28, 0x11, 0x3f, 0x23, 0x2a, 0xd1, 0x3e, 0x23, 0x2c, 0xd1, 0x3f, 0x23, 0x2e, + 0xc1, 0x3f, 0x83, 0x26, 0x06, 0x01, 0x83, 0x28, 0x46, 0x01, 0x03, 0x2e, 0x86, 0x01, 0x03, 0x26, 0xc6, 0x01, 0x23, + 0x20, 0xa1, 0x42, 0x23, 0x22, 0x41, 0x43, 0x23, 0x24, 0xd1, 0x42, 0x23, 0x26, 0x11, 0x43, 0x13, 0x05, 0x01, 0x33, + 0x23, 0x28, 0x01, 0x41, 0x23, 0x2a, 0xf1, 0x40, 0x23, 0x2c, 0xe1, 0x40, 0x23, 0x2e, 0x51, 0x40, 0x83, 0xa6, 0x00, + 0x00, 0x03, 0xa7, 0x40, 0x00, 0x83, 0xa7, 0x80, 0x00, 0x03, 0xa8, 0xc0, 0x00, 0x23, 0x28, 0xc1, 0x43, 0x23, 0x2a, + 0xc1, 0x42, 0x23, 0x2c, 0x91, 0x42, 0x23, 0x2e, 0x81, 0x42, 0x03, 0xa6, 0x00, 0x01, 0x83, 0xa8, 0x40, 0x01, 0x83, + 0xa2, 0x80, 0x01, 0x03, 0xae, 0xc0, 0x01, 0x23, 0x20, 0xf1, 0x46, 0x23, 0x22, 0x01, 0x47, 0x23, 0x24, 0xc1, 0x46, + 0x23, 0x26, 0x11, 0x47, 0x37, 0xb6, 0x02, 0x00, 0x23, 0x28, 0x71, 0x44, 0x23, 0x2a, 0x61, 0x44, 0x23, 0x2c, 0xd1, + 0x44, 0x23, 0x2e, 0xe1, 0x44, 0x83, 0xa6, 0x05, 0x00, 0x03, 0xa7, 0x45, 0x00, 0x83, 0xa7, 0x85, 0x00, 0x03, 0xa8, + 0xc5, 0x00, 0x23, 0x28, 0x51, 0x46, 0x23, 0x2a, 0xc1, 0x47, 0x23, 0x2c, 0x81, 0x47, 0x23, 0x2e, 0x31, 0x47, 0x83, + 0xa8, 0x05, 0x01, 0x83, 0xa2, 0x45, 0x01, 0x03, 0xa3, 0x85, 0x01, 0x83, 0xa3, 0xc5, 0x01, 0x23, 0x20, 0xf1, 0x4a, + 0x23, 0x22, 0x01, 0x4b, 0x23, 0x24, 0x11, 0x4b, 0x23, 0x26, 0x51, 0x4a, 0xb7, 0xb5, 0x02, 0x00, 0x23, 0x28, 0xf1, + 0x49, 0x23, 0x2a, 0xe1, 0x49, 0x23, 0x2c, 0xd1, 0x48, 0x23, 0x2e, 0xe1, 0x48, 0x83, 0x26, 0x05, 0x01, 0x03, 0x27, + 0x45, 0x01, 0x83, 0x27, 0x85, 0x01, 0x03, 0x28, 0xc5, 0x01, 0x23, 0x28, 0x61, 0x4a, 0x23, 0x2a, 0x71, 0x4a, 0x23, + 0x2c, 0x91, 0x4b, 0x23, 0x2e, 0x71, 0x4b, 0x83, 0x28, 0x05, 0x00, 0x83, 0x22, 0x45, 0x00, 0x03, 0x23, 0x85, 0x00, + 0x03, 0x25, 0xc5, 0x00, 0x23, 0x28, 0xf1, 0x4e, 0x23, 0x2a, 0x01, 0x4f, 0x23, 0x20, 0x61, 0x4e, 0x23, 0x22, 0xa1, + 0x4e, 0x23, 0x24, 0xd1, 0x4e, 0x23, 0x26, 0xe1, 0x4e, 0x23, 0x28, 0x21, 0x4d, 0x23, 0x2a, 0x61, 0x4d, 0x23, 0x2c, + 0x11, 0x4d, 0x23, 0x2e, 0x51, 0x4c, 0x03, 0x40, 0x56, 0x91, 0x03, 0xa5, 0x05, 0x91, 0x37, 0x06, 0x02, 0x00, 0x13, + 0x06, 0x96, 0xe7, 0x63, 0x6e, 0xc5, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x86, 0xf0, 0x13, 0x05, 0x10, 0x00, + 0x93, 0x05, 0x80, 0x18, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0x00, 0x43, 0x13, 0x06, 0x00, 0x00, 0x93, 0x06, 0x85, + 0x18, 0x23, 0xa8, 0xd5, 0x90, 0xb7, 0xb5, 0x00, 0x00, 0x93, 0x85, 0x05, 0x91, 0x93, 0x06, 0x80, 0x18, 0x33, 0x85, + 0xa5, 0x00, 0x13, 0x04, 0x01, 0x37, 0x23, 0x2e, 0xd1, 0x4e, 0x23, 0x20, 0xa1, 0x50, 0x23, 0x22, 0x01, 0x50, 0x93, + 0x04, 0x81, 0x4f, 0x83, 0x25, 0xc1, 0x4f, 0x83, 0x29, 0x04, 0x00, 0x03, 0x29, 0x44, 0x00, 0xb3, 0x85, 0xc5, 0x40, + 0x63, 0xfa, 0xba, 0x04, 0xb3, 0x05, 0xc5, 0x00, 0x93, 0xd6, 0x89, 0x01, 0x13, 0xd7, 0x09, 0x01, 0x93, 0xd7, 0x89, + 0x00, 0x13, 0x58, 0x89, 0x01, 0x93, 0x58, 0x09, 0x01, 0x23, 0x80, 0x35, 0x01, 0xa3, 0x80, 0xf5, 0x00, 0x23, 0x81, + 0xe5, 0x00, 0xa3, 0x81, 0xd5, 0x00, 0x93, 0x56, 0x89, 0x00, 0x13, 0x06, 0x86, 0x00, 0x13, 0x04, 0x84, 0x00, 0x23, + 0x82, 0x25, 0x01, 0xa3, 0x82, 0xd5, 0x00, 0x23, 0x83, 0x15, 0x01, 0xa3, 0x83, 0x05, 0x01, 0x23, 0x22, 0xc1, 0x50, + 0xe3, 0x12, 0x94, 0xfa, 0x6f, 0x00, 0x00, 0x02, 0x13, 0x05, 0xc1, 0x4f, 0x93, 0x05, 0x06, 0x00, 0x97, 0xe0, 0xff, + 0xff, 0xe7, 0x80, 0x40, 0x3c, 0x03, 0x25, 0x01, 0x50, 0x03, 0x26, 0x41, 0x50, 0x6f, 0xf0, 0x9f, 0xf9, 0x83, 0x25, + 0x01, 0x50, 0x13, 0x05, 0x81, 0x50, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x23, 0x03, 0x25, 0x81, 0x50, 0x83, + 0x25, 0xc1, 0x50, 0x03, 0x26, 0x01, 0x51, 0x83, 0x26, 0x41, 0x51, 0x03, 0x27, 0x81, 0x51, 0x83, 0x27, 0xc1, 0x51, + 0x03, 0x28, 0x01, 0x52, 0x83, 0x28, 0x41, 0x52, 0x83, 0x22, 0x01, 0x18, 0x93, 0x92, 0x02, 0x01, 0x23, 0x28, 0xa1, + 0x34, 0x23, 0x2a, 0xb1, 0x34, 0x23, 0x2c, 0xc1, 0x34, 0x23, 0x2e, 0xd1, 0x34, 0x23, 0x20, 0xe1, 0x36, 0x23, 0x22, + 0xf1, 0x36, 0x23, 0x24, 0x01, 0x37, 0x23, 0x26, 0x11, 0x37, 0x13, 0xd6, 0x02, 0x01, 0x13, 0x05, 0x01, 0x37, 0x93, + 0x05, 0x01, 0x35, 0x13, 0x07, 0x00, 0x01, 0x83, 0x26, 0xc1, 0x00, 0x97, 0xe0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0x68, + 0x03, 0x25, 0x01, 0x37, 0x83, 0x25, 0x41, 0x37, 0x03, 0x27, 0x01, 0x01, 0x03, 0x26, 0x47, 0x00, 0x83, 0x26, 0x07, + 0x00, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x10, 0x05, 0x0a, 0x03, 0x25, + 0x81, 0x37, 0x83, 0x25, 0xc1, 0x37, 0x03, 0x26, 0xc7, 0x00, 0x83, 0x26, 0x87, 0x00, 0xb3, 0xc5, 0xc5, 0x00, 0x33, + 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x10, 0x05, 0x08, 0x03, 0x25, 0x01, 0x38, 0x83, 0x25, 0x41, 0x38, + 0x03, 0x26, 0x47, 0x01, 0x83, 0x26, 0x07, 0x01, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, + 0x00, 0x63, 0x10, 0x05, 0x06, 0x03, 0x25, 0x81, 0x38, 0x83, 0x25, 0xc1, 0x38, 0x03, 0x26, 0xc7, 0x01, 0x83, 0x26, + 0x87, 0x01, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x10, 0x05, 0x04, 0x83, + 0x20, 0xc1, 0x55, 0x03, 0x24, 0x81, 0x55, 0x83, 0x24, 0x41, 0x55, 0x03, 0x29, 0x01, 0x55, 0x83, 0x29, 0xc1, 0x54, + 0x03, 0x2a, 0x81, 0x54, 0x83, 0x2a, 0x41, 0x54, 0x03, 0x2b, 0x01, 0x54, 0x83, 0x2b, 0xc1, 0x53, 0x03, 0x2c, 0x81, + 0x53, 0x83, 0x2c, 0x41, 0x53, 0x03, 0x2d, 0x01, 0x53, 0x83, 0x2d, 0xc1, 0x52, 0x13, 0x01, 0x01, 0x56, 0x67, 0x80, + 0x00, 0x00, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0xc5, 0xf5, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, 0xf9, 0x93, + 0x05, 0x20, 0x03, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x3c, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x85, 0xf1, + 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, 0xf4, 0x93, 0x05, 0x40, 0x03, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0xc0, + 0x3a, 0x13, 0x01, 0x01, 0xd9, 0x23, 0x26, 0x11, 0x26, 0x23, 0x24, 0x81, 0x26, 0x23, 0x22, 0x91, 0x26, 0x23, 0x20, + 0x21, 0x27, 0x23, 0x2e, 0x31, 0x25, 0x23, 0x2c, 0x41, 0x25, 0x23, 0x2a, 0x51, 0x25, 0x23, 0x28, 0x61, 0x25, 0x23, + 0x26, 0x71, 0x25, 0x23, 0x24, 0x81, 0x25, 0x23, 0x22, 0x91, 0x25, 0x23, 0x20, 0xa1, 0x25, 0x23, 0x2e, 0xb1, 0x23, + 0x13, 0x89, 0x06, 0x00, 0x93, 0x04, 0x06, 0x00, 0x93, 0x89, 0x05, 0x00, 0x13, 0x04, 0x05, 0x00, 0x13, 0x8b, 0x76, + 0x00, 0x13, 0x5a, 0x3b, 0x00, 0x13, 0x05, 0x81, 0x00, 0x13, 0x06, 0x00, 0x20, 0x93, 0x0a, 0x81, 0x00, 0x93, 0x05, + 0x00, 0x00, 0x97, 0x80, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xeb, 0x93, 0x05, 0x00, 0x00, 0x13, 0x06, 0x89, 0xff, 0x93, + 0x76, 0x8b, 0xff, 0x93, 0x89, 0x79, 0x00, 0x13, 0x07, 0x80, 0x00, 0x93, 0x07, 0x70, 0x00, 0x13, 0x08, 0x30, 0x00, + 0x93, 0x08, 0x50, 0x00, 0x93, 0x02, 0x10, 0x00, 0x13, 0x03, 0x90, 0x00, 0x93, 0x03, 0x80, 0x00, 0x6f, 0x00, 0xc0, + 0x06, 0x13, 0x05, 0x00, 0x00, 0x13, 0x0c, 0x00, 0x00, 0x93, 0x0b, 0x00, 0x00, 0x93, 0x0f, 0x00, 0x00, 0x13, 0x0b, + 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, 0x13, 0x0e, 0x00, 0x00, 0x13, 0x15, 0x85, 0x01, 0x13, + 0x1c, 0x0c, 0x01, 0x93, 0x9b, 0x8b, 0x00, 0x13, 0x1b, 0x8b, 0x01, 0x13, 0x1f, 0x0f, 0x01, 0x93, 0x9e, 0x8e, 0x00, + 0x33, 0x65, 0xac, 0x00, 0x33, 0x8c, 0xba, 0x00, 0x93, 0x85, 0x85, 0x00, 0xb3, 0xef, 0xfb, 0x01, 0x33, 0x6f, 0xeb, + 0x01, 0x33, 0xee, 0xce, 0x01, 0x33, 0x65, 0xf5, 0x01, 0x33, 0x6e, 0xcf, 0x01, 0x23, 0x20, 0xcc, 0x01, 0x23, 0x22, + 0xac, 0x00, 0x93, 0x83, 0x83, 0xff, 0x63, 0x86, 0xb6, 0x12, 0x93, 0x8d, 0x05, 0x00, 0x63, 0xe4, 0xc5, 0x00, 0x93, + 0x0d, 0x06, 0x00, 0x33, 0x8d, 0xb5, 0x41, 0xe3, 0x04, 0xed, 0xf8, 0x63, 0xf0, 0x95, 0x28, 0xb3, 0x8c, 0xb9, 0x00, + 0x03, 0xce, 0x9c, 0xff, 0x63, 0x12, 0xfd, 0x02, 0x13, 0x05, 0x00, 0x00, 0x13, 0x0c, 0x00, 0x00, 0x93, 0x0b, 0x00, + 0x00, 0x93, 0x0f, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, 0x6f, 0xf0, + 0x9f, 0xf7, 0x13, 0x85, 0x15, 0x00, 0x63, 0x78, 0x95, 0x24, 0x83, 0xce, 0xac, 0xff, 0xb3, 0x8d, 0x7d, 0x00, 0x63, + 0xe6, 0x0d, 0x03, 0x13, 0x85, 0x25, 0x00, 0x63, 0x7e, 0x95, 0x22, 0x03, 0xcf, 0xbc, 0xff, 0x63, 0x1c, 0x1d, 0x03, + 0x13, 0x05, 0x00, 0x00, 0x13, 0x0c, 0x00, 0x00, 0x93, 0x0b, 0x00, 0x00, 0x93, 0x0f, 0x00, 0x00, 0x13, 0x0b, 0x00, + 0x00, 0x6f, 0xf0, 0xdf, 0xf3, 0x13, 0x05, 0x00, 0x00, 0x13, 0x0c, 0x00, 0x00, 0x93, 0x0b, 0x00, 0x00, 0x93, 0x0f, + 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00, 0x6f, 0xf0, 0x1f, 0xf2, 0x13, 0x85, 0x35, 0x00, 0x63, + 0x7c, 0x95, 0x1e, 0x03, 0xcb, 0xcc, 0xff, 0x63, 0xe2, 0x1d, 0x03, 0x13, 0x85, 0x45, 0x00, 0x63, 0x74, 0x95, 0x1e, + 0x83, 0xcf, 0xdc, 0xff, 0x63, 0x14, 0x0d, 0x03, 0x13, 0x05, 0x00, 0x00, 0x13, 0x0c, 0x00, 0x00, 0x93, 0x0b, 0x00, + 0x00, 0x6f, 0xf0, 0x1f, 0xef, 0x13, 0x05, 0x00, 0x00, 0x13, 0x0c, 0x00, 0x00, 0x93, 0x0b, 0x00, 0x00, 0x93, 0x0f, + 0x00, 0x00, 0x6f, 0xf0, 0xdf, 0xed, 0x13, 0x85, 0x55, 0x00, 0x63, 0x7a, 0x95, 0x1a, 0x83, 0xcb, 0xec, 0xff, 0x63, + 0xee, 0xfd, 0x00, 0x13, 0x85, 0x65, 0x00, 0x63, 0x72, 0x95, 0x1a, 0x03, 0xcc, 0xfc, 0xff, 0x63, 0x1c, 0x5d, 0x00, + 0x13, 0x05, 0x00, 0x00, 0x6f, 0xf0, 0x5f, 0xeb, 0x13, 0x05, 0x00, 0x00, 0x13, 0x0c, 0x00, 0x00, 0x6f, 0xf0, 0x9f, + 0xea, 0x13, 0x85, 0x75, 0x00, 0x63, 0x70, 0x95, 0x18, 0x63, 0xf4, 0x6d, 0x1a, 0x03, 0xc5, 0x0c, 0x00, 0x6f, 0xf0, + 0x5f, 0xe9, 0x93, 0x14, 0x3a, 0x00, 0x93, 0x09, 0x81, 0x00, 0xb7, 0xb5, 0x02, 0x00, 0x37, 0xb5, 0x02, 0x00, 0x33, + 0x86, 0x99, 0x00, 0x23, 0x20, 0x26, 0x01, 0x23, 0x22, 0x06, 0x00, 0x03, 0xc0, 0x55, 0x91, 0x83, 0x25, 0x05, 0x91, + 0x37, 0x06, 0x02, 0x00, 0x93, 0x84, 0x84, 0x00, 0xb3, 0x86, 0x95, 0x00, 0x13, 0x06, 0x16, 0x00, 0x63, 0xf8, 0xc6, + 0x14, 0x13, 0x06, 0x00, 0x00, 0x23, 0x28, 0xd5, 0x90, 0x37, 0xb5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x91, 0x33, 0x05, + 0xb5, 0x00, 0x23, 0x26, 0x91, 0x20, 0x23, 0x28, 0xa1, 0x20, 0x23, 0x2a, 0x01, 0x20, 0x13, 0x09, 0x70, 0x00, 0x83, + 0x25, 0xc1, 0x20, 0x83, 0xaa, 0x09, 0x00, 0x03, 0xaa, 0x49, 0x00, 0xb3, 0x85, 0xc5, 0x40, 0x63, 0x7c, 0xb9, 0x04, + 0x93, 0x89, 0x89, 0x00, 0xb3, 0x05, 0xc5, 0x00, 0x93, 0xd6, 0x8a, 0x01, 0x13, 0xd7, 0x0a, 0x01, 0x93, 0xd7, 0x8a, + 0x00, 0x13, 0x58, 0x8a, 0x01, 0x93, 0x58, 0x0a, 0x01, 0x23, 0x80, 0x55, 0x01, 0xa3, 0x80, 0xf5, 0x00, 0x23, 0x81, + 0xe5, 0x00, 0xa3, 0x81, 0xd5, 0x00, 0x93, 0x56, 0x8a, 0x00, 0x13, 0x06, 0x86, 0x00, 0x93, 0x84, 0x84, 0xff, 0x23, + 0x82, 0x45, 0x01, 0xa3, 0x82, 0xd5, 0x00, 0x23, 0x83, 0x15, 0x01, 0xa3, 0x83, 0x05, 0x01, 0x23, 0x2a, 0xc1, 0x20, + 0xe3, 0x90, 0x04, 0xfa, 0x6f, 0x00, 0x00, 0x02, 0x13, 0x05, 0xc1, 0x20, 0x93, 0x05, 0x06, 0x00, 0x97, 0xe0, 0xff, + 0xff, 0xe7, 0x80, 0x00, 0xf5, 0x03, 0x25, 0x01, 0x21, 0x03, 0x26, 0x41, 0x21, 0x6f, 0xf0, 0x5f, 0xf9, 0x83, 0x25, + 0x01, 0x21, 0x13, 0x05, 0x81, 0x21, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xdc, 0x03, 0x25, 0x81, 0x21, 0x83, + 0x25, 0xc1, 0x21, 0x03, 0x26, 0x01, 0x22, 0x83, 0x26, 0x41, 0x22, 0x03, 0x27, 0x81, 0x22, 0x83, 0x27, 0xc1, 0x22, + 0x03, 0x28, 0x01, 0x23, 0x83, 0x28, 0x41, 0x23, 0x23, 0x20, 0xa4, 0x00, 0x23, 0x22, 0xb4, 0x00, 0x23, 0x24, 0xc4, + 0x00, 0x23, 0x26, 0xd4, 0x00, 0x23, 0x28, 0xe4, 0x00, 0x23, 0x2a, 0xf4, 0x00, 0x23, 0x2c, 0x04, 0x01, 0x23, 0x2e, + 0x14, 0x01, 0x83, 0x20, 0xc1, 0x26, 0x03, 0x24, 0x81, 0x26, 0x83, 0x24, 0x41, 0x26, 0x03, 0x29, 0x01, 0x26, 0x83, + 0x29, 0xc1, 0x25, 0x03, 0x2a, 0x81, 0x25, 0x83, 0x2a, 0x41, 0x25, 0x03, 0x2b, 0x01, 0x25, 0x83, 0x2b, 0xc1, 0x24, + 0x03, 0x2c, 0x81, 0x24, 0x83, 0x2c, 0x41, 0x24, 0x03, 0x2d, 0x01, 0x24, 0x83, 0x2d, 0xc1, 0x23, 0x13, 0x01, 0x01, + 0x27, 0x67, 0x80, 0x00, 0x00, 0x13, 0x85, 0x05, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, 0xfa, 0x93, 0x85, + 0x04, 0x00, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x04, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x86, 0xf0, 0x13, + 0x05, 0x10, 0x00, 0x93, 0x85, 0x04, 0x00, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xe5, 0x13, 0x85, 0x85, 0x00, + 0xe3, 0x78, 0x95, 0xfc, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, 0xfb, 0x13, 0x05, 0x80, 0x00, 0x93, 0x05, 0x80, + 0x00, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0x00, 0x01, 0x37, 0xb6, 0x02, 0x00, 0x03, 0x26, 0x06, 0x91, 0x33, 0x86, + 0xc5, 0x00, 0x13, 0x06, 0xf6, 0xff, 0xb3, 0x05, 0xb0, 0x40, 0xb3, 0x75, 0xb6, 0x00, 0x33, 0x85, 0xa5, 0x00, 0x63, + 0x66, 0xb5, 0x00, 0x37, 0x06, 0x02, 0x00, 0x63, 0x76, 0xa6, 0x00, 0x13, 0x05, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, + 0x37, 0xb6, 0x02, 0x00, 0x23, 0x28, 0xa6, 0x90, 0x37, 0xb5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x91, 0x33, 0x05, 0xb5, + 0x00, 0x67, 0x80, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 0x37, 0xb7, 0x02, 0x00, 0x03, 0x27, 0x07, 0x91, 0x33, 0x07, + 0xe6, 0x00, 0x13, 0x07, 0xf7, 0xff, 0x33, 0x06, 0xc0, 0x40, 0x33, 0x77, 0xc7, 0x00, 0xb3, 0x07, 0xd7, 0x00, 0x63, + 0xe8, 0xe7, 0x00, 0x13, 0x06, 0x05, 0x00, 0x37, 0x05, 0x02, 0x00, 0x63, 0x76, 0xf5, 0x00, 0x13, 0x05, 0x00, 0x00, + 0x67, 0x80, 0x00, 0x00, 0x37, 0xb5, 0x02, 0x00, 0x23, 0x28, 0xf5, 0x90, 0x37, 0xb5, 0x00, 0x00, 0x13, 0x05, 0x05, + 0x91, 0x33, 0x05, 0xe5, 0x00, 0x63, 0xe4, 0xb6, 0x00, 0x93, 0x86, 0x05, 0x00, 0x93, 0x05, 0x06, 0x00, 0x13, 0x86, + 0x06, 0x00, 0x17, 0x83, 0x00, 0x00, 0x67, 0x00, 0x03, 0xa7, 0x13, 0x01, 0x01, 0x81, 0x23, 0x26, 0x11, 0x7e, 0x23, + 0x24, 0x81, 0x7e, 0x23, 0x22, 0x91, 0x7e, 0x23, 0x20, 0x21, 0x7f, 0x23, 0x2e, 0x31, 0x7d, 0x23, 0x2c, 0x41, 0x7d, + 0x23, 0x2a, 0x51, 0x7d, 0x23, 0x28, 0x61, 0x7d, 0x23, 0x26, 0x71, 0x7d, 0x23, 0x24, 0x81, 0x7d, 0x23, 0x22, 0x91, + 0x7d, 0x23, 0x20, 0xa1, 0x7d, 0x23, 0x2e, 0xb1, 0x7b, 0x13, 0x01, 0x01, 0xf1, 0x83, 0x26, 0x40, 0x10, 0x03, 0x27, + 0x80, 0x10, 0x83, 0x27, 0xc0, 0x10, 0x03, 0x28, 0x00, 0x11, 0x83, 0x28, 0x40, 0x11, 0x83, 0x22, 0x80, 0x11, 0x03, + 0x23, 0xc0, 0x11, 0x83, 0x23, 0x00, 0x12, 0x23, 0x20, 0xd1, 0x14, 0x23, 0x22, 0xe1, 0x14, 0x23, 0x24, 0xf1, 0x14, + 0x23, 0x26, 0x01, 0x15, 0x23, 0x28, 0x11, 0x15, 0x23, 0x2a, 0x51, 0x14, 0x23, 0x2c, 0x61, 0x14, 0x23, 0x2e, 0x71, + 0x14, 0x83, 0x2a, 0x40, 0x12, 0x03, 0x2a, 0x80, 0x12, 0x83, 0x2b, 0xc0, 0x12, 0x83, 0x2d, 0x00, 0x13, 0x03, 0x2b, + 0x40, 0x13, 0x83, 0x29, 0x80, 0x13, 0x83, 0x2c, 0xc0, 0x13, 0x03, 0x2c, 0x00, 0x14, 0x83, 0x24, 0x40, 0x14, 0x03, + 0x24, 0x80, 0x14, 0x83, 0x20, 0xc0, 0x14, 0x03, 0x29, 0x00, 0x15, 0x83, 0x2f, 0x40, 0x15, 0x03, 0x2f, 0x80, 0x15, + 0x83, 0x2e, 0xc0, 0x15, 0x03, 0x2e, 0x00, 0x16, 0x03, 0x25, 0x40, 0x16, 0x23, 0x20, 0xa1, 0x12, 0x03, 0x25, 0x80, + 0x16, 0x23, 0x22, 0xa1, 0x0e, 0x03, 0x25, 0xc0, 0x16, 0x23, 0x20, 0xa1, 0x0e, 0x03, 0x25, 0x00, 0x17, 0x23, 0x2c, + 0xa1, 0x12, 0x03, 0x25, 0x40, 0x17, 0x23, 0x2a, 0xa1, 0x12, 0x03, 0x25, 0x80, 0x17, 0x23, 0x28, 0xa1, 0x12, 0x03, + 0x25, 0xc0, 0x17, 0x23, 0x26, 0xa1, 0x12, 0x03, 0x25, 0x00, 0x18, 0x23, 0x24, 0xa1, 0x12, 0x03, 0x25, 0x40, 0x18, + 0x23, 0x22, 0xa1, 0x12, 0x03, 0x26, 0x80, 0x18, 0x13, 0x05, 0xc0, 0x18, 0x93, 0x05, 0x50, 0x00, 0x23, 0x2e, 0xa1, + 0x12, 0x63, 0x64, 0xb6, 0x00, 0x6f, 0x20, 0xd0, 0x78, 0x23, 0x28, 0x91, 0x0e, 0x23, 0x2a, 0x81, 0x0e, 0x37, 0xb5, + 0x02, 0x00, 0x37, 0xbd, 0x02, 0x00, 0x03, 0x40, 0x55, 0x91, 0x83, 0x25, 0x0d, 0x91, 0x37, 0x05, 0x02, 0x00, 0x13, + 0x05, 0x95, 0xfd, 0x63, 0xee, 0xa5, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x86, 0xf0, 0x13, 0x05, 0x10, 0x00, + 0x93, 0x05, 0x80, 0x02, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xc2, 0x23, 0x24, 0x11, 0x06, 0x23, 0x26, 0x21, + 0x07, 0x23, 0x28, 0xf1, 0x07, 0x23, 0x2a, 0xe1, 0x07, 0x23, 0x2c, 0xd1, 0x07, 0x23, 0x2e, 0xc1, 0x07, 0x23, 0x2e, + 0x71, 0x0c, 0x23, 0x26, 0x61, 0x0e, 0x23, 0x2c, 0x51, 0x0e, 0x23, 0x2e, 0x11, 0x0f, 0x23, 0x20, 0x01, 0x11, 0x23, + 0x24, 0xf1, 0x0e, 0x23, 0x22, 0xe1, 0x10, 0x23, 0x24, 0xd1, 0x10, 0x23, 0x20, 0xc1, 0x08, 0x13, 0x85, 0x85, 0x02, + 0x37, 0xb6, 0x00, 0x00, 0x13, 0x06, 0x06, 0x91, 0x93, 0xd6, 0x8a, 0x01, 0x23, 0x2e, 0xd1, 0x10, 0x93, 0xd4, 0x0a, + 0x01, 0x13, 0xd4, 0x8a, 0x00, 0x93, 0x56, 0x8a, 0x01, 0x23, 0x26, 0xd1, 0x10, 0x93, 0x56, 0x0a, 0x01, 0x23, 0x2c, + 0xd1, 0x0c, 0x93, 0x56, 0x8a, 0x00, 0x23, 0x2c, 0xd1, 0x10, 0x93, 0xdf, 0x8b, 0x01, 0x93, 0xd6, 0x0b, 0x01, 0x23, + 0x2a, 0xd1, 0x10, 0x93, 0xd6, 0x8b, 0x00, 0x23, 0x28, 0xd1, 0x10, 0x93, 0xde, 0x8d, 0x01, 0x13, 0xd9, 0x0d, 0x01, + 0x93, 0xd6, 0x8d, 0x00, 0x93, 0x52, 0x8b, 0x01, 0x13, 0x5e, 0x0b, 0x01, 0x93, 0x50, 0x8b, 0x00, 0x93, 0xd8, 0x89, + 0x01, 0x93, 0xd3, 0x09, 0x01, 0x13, 0xdf, 0x89, 0x00, 0x93, 0xd7, 0x8c, 0x01, 0x13, 0xd8, 0x0c, 0x01, 0x13, 0xd3, + 0x8c, 0x00, 0x13, 0x57, 0x8c, 0x01, 0x23, 0x28, 0xad, 0x90, 0x13, 0x55, 0x0c, 0x01, 0xb3, 0x05, 0xb6, 0x00, 0x13, + 0x06, 0x40, 0x00, 0x23, 0x80, 0xc5, 0x00, 0x13, 0x56, 0x8c, 0x00, 0x23, 0x82, 0x05, 0x00, 0xa3, 0x82, 0x05, 0x00, + 0x23, 0x83, 0x05, 0x00, 0xa3, 0x83, 0x05, 0x00, 0xa3, 0x80, 0x05, 0x00, 0x23, 0x81, 0x05, 0x00, 0xa3, 0x81, 0x05, + 0x00, 0x23, 0x24, 0x51, 0x09, 0x23, 0x84, 0x55, 0x01, 0x23, 0x26, 0x81, 0x08, 0xa3, 0x84, 0x85, 0x00, 0x23, 0x2a, + 0x91, 0x08, 0x23, 0x85, 0x95, 0x00, 0x83, 0x2a, 0x81, 0x0d, 0x03, 0x24, 0xc1, 0x11, 0xa3, 0x85, 0x85, 0x00, 0x23, + 0x86, 0x45, 0x01, 0x03, 0x24, 0x81, 0x11, 0xa3, 0x86, 0x85, 0x00, 0x13, 0x84, 0x0a, 0x00, 0x23, 0x87, 0x55, 0x01, + 0x83, 0x2a, 0xc1, 0x10, 0xa3, 0x87, 0x55, 0x01, 0x23, 0x88, 0x75, 0x01, 0x83, 0x24, 0x01, 0x11, 0xa3, 0x88, 0x95, + 0x00, 0x83, 0x24, 0x41, 0x11, 0x23, 0x89, 0x95, 0x00, 0x23, 0x22, 0xf1, 0x0b, 0xa3, 0x89, 0xf5, 0x01, 0x23, 0x28, + 0xb1, 0x09, 0x23, 0x8a, 0xb5, 0x01, 0x23, 0x2c, 0xd1, 0x08, 0xa3, 0x8a, 0xd5, 0x00, 0x23, 0x2e, 0x21, 0x09, 0x23, + 0x8b, 0x25, 0x01, 0x23, 0x26, 0xd1, 0x0b, 0xa3, 0x8b, 0xd5, 0x01, 0x23, 0x8c, 0x65, 0x01, 0x23, 0x20, 0x11, 0x0a, + 0xa3, 0x8c, 0x15, 0x00, 0x23, 0x28, 0xc1, 0x0b, 0x23, 0x8d, 0xc5, 0x01, 0x23, 0x20, 0x51, 0x0c, 0xa3, 0x8d, 0x55, + 0x00, 0x23, 0x8e, 0x35, 0x01, 0x23, 0x24, 0xe1, 0x0b, 0xa3, 0x8e, 0xe5, 0x01, 0x23, 0x2a, 0x71, 0x0a, 0x23, 0x8f, + 0x75, 0x00, 0x23, 0x22, 0x11, 0x0d, 0xa3, 0x8f, 0x15, 0x01, 0x93, 0x84, 0x0c, 0x00, 0x23, 0x80, 0x95, 0x03, 0x23, + 0x2e, 0x61, 0x0a, 0xa3, 0x80, 0x65, 0x02, 0x23, 0x24, 0x01, 0x0d, 0x23, 0x81, 0x05, 0x03, 0x23, 0x28, 0xf1, 0x0c, + 0xa3, 0x81, 0xf5, 0x02, 0x23, 0x22, 0x81, 0x09, 0x23, 0x82, 0x85, 0x03, 0x23, 0x2c, 0xc1, 0x0a, 0xa3, 0x82, 0xc5, + 0x02, 0x23, 0x26, 0xa1, 0x0c, 0x23, 0x83, 0xa5, 0x02, 0x23, 0x2a, 0xe1, 0x0c, 0xa3, 0x83, 0xe5, 0x02, 0x13, 0x05, + 0x01, 0x79, 0x13, 0x06, 0x80, 0x02, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x90, 0x03, 0x23, 0x01, 0x79, 0x83, + 0x22, 0x41, 0x79, 0x83, 0x28, 0x81, 0x79, 0x03, 0x28, 0xc1, 0x79, 0x83, 0x27, 0x01, 0x7a, 0x03, 0x27, 0x41, 0x7a, + 0x83, 0x26, 0x81, 0x7a, 0x03, 0x26, 0xc1, 0x7a, 0x37, 0xb5, 0x02, 0x00, 0x03, 0x40, 0x55, 0x91, 0x37, 0xb5, 0x02, + 0x00, 0x83, 0x25, 0x05, 0x91, 0x37, 0x05, 0x02, 0x00, 0x13, 0x05, 0x95, 0xfb, 0x63, 0xee, 0xa5, 0x00, 0x37, 0xa6, + 0x00, 0x00, 0x13, 0x06, 0x86, 0xf0, 0x13, 0x05, 0x10, 0x00, 0x93, 0x05, 0x80, 0x04, 0x97, 0x70, 0x00, 0x00, 0xe7, + 0x80, 0x80, 0x9e, 0x23, 0x24, 0x61, 0x04, 0x23, 0x26, 0x51, 0x04, 0x23, 0x28, 0x11, 0x05, 0x23, 0x2a, 0x01, 0x05, + 0x23, 0x2c, 0xf1, 0x04, 0x23, 0x2e, 0xe1, 0x04, 0x23, 0x20, 0xd1, 0x06, 0x23, 0x22, 0xc1, 0x06, 0x13, 0x85, 0x85, + 0x04, 0x37, 0xb6, 0x02, 0x00, 0x23, 0x28, 0xa6, 0x90, 0x37, 0xb5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x91, 0xb3, 0x05, + 0xb5, 0x00, 0x13, 0x05, 0x60, 0x00, 0x23, 0x80, 0xa5, 0x00, 0x03, 0x25, 0x81, 0x08, 0x23, 0x84, 0xa5, 0x02, 0x03, + 0x25, 0xc1, 0x08, 0xa3, 0x84, 0xa5, 0x02, 0x03, 0x25, 0x41, 0x09, 0x23, 0x85, 0xa5, 0x02, 0x03, 0x25, 0xc1, 0x11, + 0xa3, 0x85, 0xa5, 0x02, 0x23, 0x86, 0x45, 0x03, 0x03, 0x25, 0x81, 0x11, 0xa3, 0x86, 0xa5, 0x02, 0x23, 0x87, 0x85, + 0x02, 0x03, 0x25, 0xc1, 0x10, 0xa3, 0x87, 0xa5, 0x02, 0x23, 0x88, 0x75, 0x03, 0x03, 0x25, 0x81, 0x10, 0x13, 0x56, + 0x85, 0x01, 0x23, 0x2e, 0xc1, 0x10, 0x03, 0x26, 0x01, 0x11, 0xa3, 0x88, 0xc5, 0x02, 0x13, 0x5a, 0x05, 0x01, 0x03, + 0x26, 0x41, 0x11, 0x23, 0x89, 0xc5, 0x02, 0x13, 0x5f, 0x85, 0x00, 0x03, 0x26, 0x41, 0x0a, 0xa3, 0x89, 0xc5, 0x02, + 0x03, 0x26, 0x41, 0x10, 0x13, 0x5c, 0x86, 0x01, 0x83, 0x26, 0x01, 0x09, 0x23, 0x8a, 0xd5, 0x02, 0x13, 0x54, 0x06, + 0x01, 0x83, 0x26, 0x81, 0x09, 0xa3, 0x8a, 0xd5, 0x02, 0x93, 0x5a, 0x86, 0x00, 0x83, 0x26, 0xc1, 0x09, 0x23, 0x8b, + 0xd5, 0x02, 0x83, 0x26, 0x81, 0x0e, 0x13, 0xde, 0x86, 0x01, 0x03, 0x27, 0xc1, 0x0a, 0xa3, 0x8b, 0xe5, 0x02, 0x93, + 0xde, 0x06, 0x01, 0x23, 0x8c, 0x65, 0x03, 0x13, 0xd7, 0x86, 0x00, 0x23, 0x2c, 0xe1, 0x10, 0x03, 0x27, 0x01, 0x0a, + 0xa3, 0x8c, 0xe5, 0x02, 0x03, 0x27, 0x01, 0x10, 0x93, 0x5f, 0x87, 0x01, 0x83, 0x27, 0x01, 0x0b, 0x23, 0x8d, 0xf5, + 0x02, 0x93, 0x57, 0x07, 0x01, 0x23, 0x28, 0xf1, 0x10, 0x83, 0x27, 0x01, 0x0c, 0xa3, 0x8d, 0xf5, 0x02, 0x93, 0x57, + 0x87, 0x00, 0x23, 0x2a, 0xf1, 0x10, 0x23, 0x8e, 0x35, 0x03, 0x83, 0x27, 0xc1, 0x0f, 0x93, 0xd0, 0x87, 0x01, 0x03, + 0x28, 0x81, 0x0a, 0xa3, 0x8e, 0x05, 0x03, 0x13, 0xd8, 0x07, 0x01, 0x23, 0x26, 0x01, 0x11, 0x03, 0x28, 0x41, 0x0b, + 0x23, 0x8f, 0x05, 0x03, 0x13, 0xdd, 0x87, 0x00, 0x03, 0x28, 0x41, 0x0c, 0xa3, 0x8f, 0x05, 0x03, 0x03, 0x29, 0x81, + 0x0f, 0x13, 0x5b, 0x89, 0x01, 0x23, 0x80, 0x95, 0x04, 0x93, 0x5c, 0x09, 0x01, 0x03, 0x28, 0xc1, 0x0b, 0xa3, 0x80, + 0x05, 0x05, 0x93, 0x53, 0x89, 0x00, 0x03, 0x28, 0x81, 0x0c, 0x23, 0x81, 0x05, 0x05, 0x83, 0x2d, 0xc1, 0x0e, 0x93, + 0xd4, 0x8d, 0x01, 0x03, 0x28, 0x01, 0x0d, 0xa3, 0x81, 0x05, 0x05, 0x13, 0xd3, 0x0d, 0x01, 0x03, 0x28, 0x41, 0x08, + 0x23, 0x82, 0x05, 0x05, 0x93, 0xd2, 0x8d, 0x00, 0x03, 0x28, 0x81, 0x0b, 0xa3, 0x82, 0x05, 0x05, 0x83, 0x29, 0xc1, + 0x0d, 0x93, 0xdb, 0x89, 0x01, 0x03, 0x28, 0xc1, 0x0c, 0x23, 0x83, 0x05, 0x05, 0x93, 0xd8, 0x09, 0x01, 0x03, 0x28, + 0x41, 0x0d, 0xa3, 0x83, 0x05, 0x05, 0x13, 0xd8, 0x89, 0x00, 0x23, 0x82, 0x05, 0x00, 0xa3, 0x82, 0x05, 0x00, 0x23, + 0x83, 0x05, 0x00, 0xa3, 0x83, 0x05, 0x00, 0xa3, 0x80, 0x05, 0x00, 0x23, 0x81, 0x05, 0x00, 0xa3, 0x81, 0x05, 0x00, + 0x23, 0x84, 0xa5, 0x00, 0x23, 0x22, 0xe1, 0x0d, 0xa3, 0x84, 0xe5, 0x01, 0x23, 0x28, 0x41, 0x0d, 0x23, 0x85, 0x45, + 0x01, 0x03, 0x25, 0xc1, 0x11, 0xa3, 0x85, 0xa5, 0x00, 0x23, 0x86, 0xc5, 0x00, 0x23, 0x2c, 0x51, 0x0b, 0xa3, 0x86, + 0x55, 0x01, 0x13, 0x05, 0x0b, 0x00, 0x03, 0x2b, 0x01, 0x11, 0x83, 0x2a, 0x81, 0x11, 0x13, 0x0a, 0x0d, 0x00, 0x13, + 0x86, 0x04, 0x00, 0x03, 0x2d, 0x41, 0x11, 0x83, 0x24, 0xc1, 0x10, 0x23, 0x26, 0x81, 0x0c, 0x23, 0x87, 0x85, 0x00, + 0x23, 0x2c, 0x81, 0x0d, 0xa3, 0x87, 0x85, 0x01, 0x23, 0x88, 0xd5, 0x00, 0xa3, 0x88, 0x55, 0x01, 0x23, 0x24, 0xd1, + 0x0d, 0x23, 0x89, 0xd5, 0x01, 0x23, 0x2a, 0xc1, 0x0d, 0xa3, 0x89, 0xc5, 0x01, 0x23, 0x8a, 0xe5, 0x00, 0xa3, 0x8a, + 0xa5, 0x01, 0x23, 0x8b, 0x65, 0x01, 0x23, 0x20, 0xf1, 0x0d, 0xa3, 0x8b, 0xf5, 0x01, 0x23, 0x8c, 0xf5, 0x00, 0xa3, + 0x8c, 0x45, 0x01, 0x23, 0x8d, 0x95, 0x00, 0x23, 0x2e, 0x11, 0x0a, 0xa3, 0x8d, 0x15, 0x00, 0x23, 0x8e, 0x25, 0x01, + 0x13, 0x89, 0x03, 0x00, 0xa3, 0x8e, 0x75, 0x00, 0x23, 0x8f, 0x95, 0x01, 0x13, 0x0c, 0x05, 0x00, 0xa3, 0x8f, 0xa5, + 0x00, 0x23, 0x80, 0xb5, 0x03, 0x93, 0x8d, 0x02, 0x00, 0xa3, 0x80, 0x55, 0x02, 0x23, 0x2a, 0x61, 0x0a, 0x23, 0x81, + 0x65, 0x02, 0x13, 0x04, 0x06, 0x00, 0xa3, 0x81, 0xc5, 0x02, 0x23, 0x82, 0x35, 0x03, 0x93, 0x09, 0x08, 0x00, 0xa3, + 0x82, 0x05, 0x03, 0x23, 0x28, 0x11, 0x0b, 0x23, 0x83, 0x15, 0x03, 0xa3, 0x83, 0x75, 0x03, 0x13, 0x05, 0x01, 0x79, + 0x13, 0x06, 0x80, 0x04, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x5e, 0xb7, 0xb3, 0x02, 0x00, 0x03, 0x26, 0x01, + 0x79, 0x83, 0x26, 0x41, 0x79, 0x03, 0x27, 0x81, 0x79, 0x83, 0x27, 0xc1, 0x79, 0x03, 0x28, 0x01, 0x7a, 0x83, 0x28, + 0x41, 0x7a, 0x83, 0x22, 0x81, 0x7a, 0x03, 0x23, 0xc1, 0x7a, 0x37, 0xb5, 0x02, 0x00, 0x03, 0x40, 0x55, 0x91, 0x83, + 0xa5, 0x03, 0x91, 0x37, 0x05, 0x02, 0x00, 0x13, 0x05, 0x95, 0xf9, 0x63, 0xee, 0xa5, 0x00, 0x37, 0xa6, 0x00, 0x00, + 0x13, 0x06, 0x86, 0xf0, 0x13, 0x05, 0x10, 0x00, 0x93, 0x05, 0x80, 0x06, 0x97, 0x60, 0x00, 0x00, 0xe7, 0x80, 0xc0, + 0x6c, 0x23, 0x28, 0x61, 0x08, 0x23, 0x2a, 0x51, 0x08, 0x23, 0x2c, 0x11, 0x09, 0x23, 0x2e, 0x01, 0x09, 0x23, 0x20, + 0xf1, 0x0a, 0x23, 0x22, 0xe1, 0x0a, 0x23, 0x24, 0xd1, 0x0a, 0x23, 0x26, 0xc1, 0x0a, 0x13, 0x85, 0x85, 0x06, 0x23, + 0xa8, 0xa3, 0x90, 0x37, 0xb5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x91, 0xb3, 0x05, 0xb5, 0x00, 0x13, 0x05, 0x50, 0x00, + 0x23, 0x80, 0xa5, 0x00, 0x03, 0x25, 0x41, 0x0c, 0xa3, 0x84, 0xa5, 0x00, 0x03, 0x25, 0x01, 0x0d, 0x23, 0x85, 0xa5, + 0x00, 0x03, 0x25, 0xc1, 0x11, 0xa3, 0x85, 0xa5, 0x00, 0x03, 0x25, 0x81, 0x0b, 0xa3, 0x86, 0xa5, 0x00, 0x03, 0x25, + 0xc1, 0x0c, 0x23, 0x87, 0xa5, 0x00, 0x03, 0x25, 0x81, 0x0d, 0xa3, 0x87, 0xa5, 0x00, 0xa3, 0x88, 0x55, 0x01, 0x03, + 0x25, 0x81, 0x0c, 0x23, 0x89, 0xa5, 0x00, 0x03, 0x25, 0x41, 0x0d, 0xa3, 0x89, 0xa5, 0x00, 0xa3, 0x8a, 0xa5, 0x01, + 0x23, 0x8b, 0x65, 0x01, 0x03, 0x25, 0x01, 0x0c, 0xa3, 0x8b, 0xa5, 0x00, 0xa3, 0x8c, 0x45, 0x01, 0x23, 0x8d, 0x95, + 0x00, 0x03, 0x25, 0xc1, 0x0b, 0xa3, 0x8d, 0xa5, 0x00, 0xa3, 0x8e, 0x25, 0x01, 0x23, 0x8f, 0x95, 0x01, 0xa3, 0x8f, + 0x85, 0x01, 0xa3, 0x80, 0xb5, 0x03, 0x03, 0x25, 0x41, 0x0b, 0x23, 0x81, 0xa5, 0x02, 0xa3, 0x81, 0x85, 0x02, 0xa3, + 0x82, 0x35, 0x03, 0x03, 0x25, 0x01, 0x0b, 0x23, 0x83, 0xa5, 0x02, 0x83, 0x26, 0x81, 0x04, 0x13, 0xd5, 0x06, 0x01, + 0xa3, 0x83, 0x75, 0x03, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x84, 0xc5, 0x02, 0x23, 0x84, 0xd5, 0x02, 0x13, 0xd6, 0x86, + 0x01, 0x23, 0x85, 0xa5, 0x02, 0x83, 0x26, 0xc1, 0x04, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x85, 0xc5, 0x02, 0x13, 0xd6, + 0x86, 0x00, 0xa3, 0x86, 0xc5, 0x02, 0x23, 0x86, 0xd5, 0x02, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x87, 0xa5, 0x02, 0x83, + 0x26, 0x01, 0x05, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x87, 0xc5, 0x02, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x88, 0xc5, 0x02, + 0x23, 0x88, 0xd5, 0x02, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x89, 0xa5, 0x02, 0x83, 0x26, 0x41, 0x05, 0x13, 0xd5, 0x06, + 0x01, 0xa3, 0x89, 0xc5, 0x02, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x8a, 0xc5, 0x02, 0x23, 0x8a, 0xd5, 0x02, 0x13, 0xd6, + 0x86, 0x01, 0x23, 0x8b, 0xa5, 0x02, 0x83, 0x26, 0x81, 0x05, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x8b, 0xc5, 0x02, 0x13, + 0xd6, 0x86, 0x00, 0xa3, 0x8c, 0xc5, 0x02, 0x23, 0x8c, 0xd5, 0x02, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x8d, 0xa5, 0x02, + 0x83, 0x26, 0xc1, 0x05, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x8d, 0xc5, 0x02, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x8e, 0xc5, + 0x02, 0x23, 0x8e, 0xd5, 0x02, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x8f, 0xa5, 0x02, 0x83, 0x26, 0x01, 0x06, 0x13, 0xd5, + 0x06, 0x01, 0xa3, 0x8f, 0xc5, 0x02, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x80, 0xc5, 0x04, 0x23, 0x80, 0xd5, 0x04, 0x13, + 0xd6, 0x86, 0x01, 0x23, 0x81, 0xa5, 0x04, 0x83, 0x26, 0x41, 0x06, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x81, 0xc5, 0x04, + 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x82, 0xc5, 0x04, 0x23, 0x82, 0xd5, 0x04, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x83, 0xa5, + 0x04, 0x83, 0x26, 0x01, 0x0f, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x83, 0xc5, 0x04, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x84, + 0xc5, 0x04, 0x23, 0x84, 0xd5, 0x04, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x85, 0xa5, 0x04, 0x83, 0x26, 0x41, 0x0f, 0x13, + 0xd5, 0x06, 0x01, 0xa3, 0x85, 0xc5, 0x04, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x86, 0xc5, 0x04, 0x23, 0x86, 0xd5, 0x04, + 0x13, 0xd6, 0x86, 0x01, 0x23, 0x87, 0xa5, 0x04, 0x83, 0x26, 0x81, 0x06, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x87, 0xc5, + 0x04, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x88, 0xc5, 0x04, 0x23, 0x88, 0xd5, 0x04, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x89, + 0xa5, 0x04, 0x83, 0x26, 0xc1, 0x06, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x89, 0xc5, 0x04, 0x13, 0xd6, 0x86, 0x00, 0xa3, + 0x8a, 0xc5, 0x04, 0x23, 0x8a, 0xd5, 0x04, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x8b, 0xa5, 0x04, 0x83, 0x26, 0x01, 0x07, + 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x8b, 0xc5, 0x04, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x8c, 0xc5, 0x04, 0x23, 0x8c, 0xd5, + 0x04, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x8d, 0xa5, 0x04, 0x83, 0x26, 0x41, 0x07, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x8d, + 0xc5, 0x04, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x8e, 0xc5, 0x04, 0x23, 0x8e, 0xd5, 0x04, 0x13, 0xd6, 0x86, 0x01, 0x23, + 0x8f, 0xa5, 0x04, 0x83, 0x26, 0x81, 0x07, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x8f, 0xc5, 0x04, 0x13, 0xd6, 0x86, 0x00, + 0xa3, 0x80, 0xc5, 0x06, 0x23, 0x80, 0xd5, 0x06, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x81, 0xa5, 0x06, 0x83, 0x26, 0xc1, + 0x07, 0x13, 0xd5, 0x06, 0x01, 0xa3, 0x81, 0xc5, 0x06, 0x13, 0xd6, 0x86, 0x00, 0xa3, 0x82, 0xc5, 0x06, 0x23, 0x82, + 0xd5, 0x06, 0x13, 0xd6, 0x86, 0x01, 0x23, 0x83, 0xa5, 0x06, 0xa3, 0x83, 0xc5, 0x06, 0x03, 0x29, 0x41, 0x10, 0x23, + 0x86, 0x25, 0x01, 0x83, 0x24, 0x81, 0x10, 0x23, 0x84, 0x95, 0x00, 0x03, 0x25, 0x01, 0x10, 0x23, 0x8a, 0xa5, 0x00, + 0x03, 0x25, 0x81, 0x0e, 0x23, 0x88, 0xa5, 0x00, 0x03, 0x25, 0x81, 0x0f, 0x23, 0x8e, 0xa5, 0x00, 0x03, 0x25, 0xc1, + 0x0f, 0x23, 0x8c, 0xa5, 0x00, 0x03, 0x25, 0xc1, 0x0d, 0x23, 0x82, 0xa5, 0x02, 0x03, 0x25, 0xc1, 0x0e, 0x23, 0x80, + 0xa5, 0x02, 0x23, 0x82, 0x05, 0x00, 0xa3, 0x82, 0x05, 0x00, 0x23, 0x83, 0x05, 0x00, 0xa3, 0x83, 0x05, 0x00, 0xa3, + 0x80, 0x05, 0x00, 0x23, 0x81, 0x05, 0x00, 0xa3, 0x81, 0x05, 0x00, 0x13, 0x05, 0x01, 0x79, 0x13, 0x06, 0x80, 0x06, + 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x25, 0x03, 0x25, 0x01, 0x79, 0x83, 0x25, 0x41, 0x79, 0x03, 0x26, 0x81, + 0x79, 0x83, 0x26, 0xc1, 0x79, 0x03, 0x27, 0x01, 0x7a, 0x83, 0x27, 0x41, 0x7a, 0x03, 0x28, 0x81, 0x7a, 0x83, 0x28, + 0xc1, 0x7a, 0x23, 0x20, 0xa1, 0x16, 0x23, 0x22, 0xb1, 0x16, 0x23, 0x24, 0xc1, 0x16, 0x23, 0x26, 0xd1, 0x16, 0x23, + 0x28, 0xe1, 0x16, 0x23, 0x2a, 0xf1, 0x16, 0x23, 0x2c, 0x01, 0x17, 0x23, 0x2e, 0x11, 0x17, 0x23, 0x28, 0xe1, 0x18, + 0x23, 0x2a, 0xf1, 0x18, 0x23, 0x2c, 0x01, 0x19, 0x23, 0x2e, 0x11, 0x19, 0x23, 0x20, 0xa1, 0x18, 0x23, 0x22, 0xb1, + 0x18, 0x23, 0x24, 0xc1, 0x18, 0x23, 0x26, 0xd1, 0x18, 0x13, 0x05, 0x01, 0x1a, 0x13, 0x06, 0x00, 0x08, 0x93, 0x0b, + 0x01, 0x1a, 0x93, 0x05, 0x00, 0x00, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x02, 0x13, 0x05, 0x01, 0x22, 0x13, + 0x06, 0x00, 0x08, 0x13, 0x04, 0x01, 0x22, 0x93, 0x05, 0x00, 0x00, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x01, + 0x83, 0x2c, 0x01, 0x08, 0x63, 0x82, 0x0c, 0x76, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0a, 0x00, 0x00, 0x93, 0x0a, 0x00, + 0x00, 0x13, 0x0c, 0x10, 0x00, 0x03, 0x2d, 0x41, 0x0e, 0x83, 0x2d, 0x01, 0x0e, 0x03, 0x25, 0xc1, 0x13, 0x83, 0x24, + 0x05, 0x00, 0x03, 0x29, 0x45, 0x00, 0x83, 0x25, 0x85, 0x00, 0x03, 0x26, 0xc5, 0x00, 0x83, 0x26, 0x05, 0x01, 0x03, + 0x27, 0x45, 0x01, 0x83, 0x27, 0x85, 0x01, 0x03, 0x28, 0xc5, 0x01, 0x83, 0x28, 0x05, 0x02, 0x83, 0x22, 0x45, 0x02, + 0x23, 0x20, 0xb1, 0x2a, 0x23, 0x22, 0xc1, 0x2a, 0x23, 0x24, 0xd1, 0x2a, 0x23, 0x26, 0xe1, 0x2a, 0x23, 0x28, 0xf1, + 0x2a, 0x23, 0x2a, 0x01, 0x2b, 0x23, 0x2c, 0x11, 0x2b, 0x23, 0x2e, 0x51, 0x2a, 0x83, 0x25, 0x85, 0x02, 0x03, 0x26, + 0xc5, 0x02, 0x83, 0x26, 0x05, 0x03, 0x03, 0x27, 0x45, 0x03, 0x83, 0x27, 0x85, 0x03, 0x03, 0x28, 0xc5, 0x03, 0x83, + 0x28, 0x05, 0x04, 0x83, 0x22, 0x45, 0x04, 0x23, 0x20, 0xb1, 0x66, 0x23, 0x22, 0xc1, 0x66, 0x23, 0x24, 0xd1, 0x66, + 0x23, 0x26, 0xe1, 0x66, 0x23, 0x28, 0xf1, 0x66, 0x23, 0x2a, 0x01, 0x67, 0x23, 0x2c, 0x11, 0x67, 0x23, 0x2e, 0x51, + 0x66, 0x83, 0x29, 0x85, 0x04, 0x13, 0x05, 0xc5, 0x04, 0x23, 0x2e, 0xa1, 0x12, 0x13, 0x05, 0x01, 0x2c, 0x93, 0x05, + 0x01, 0x14, 0x13, 0x07, 0x01, 0x2a, 0x93, 0x07, 0x01, 0x16, 0x13, 0x08, 0x01, 0x66, 0x13, 0x86, 0x04, 0x00, 0x93, + 0x06, 0x09, 0x00, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0xc0, 0x30, 0x03, 0x25, 0x01, 0x2d, 0x83, 0x25, 0x41, 0x2d, + 0x03, 0x26, 0x81, 0x2d, 0x83, 0x26, 0xc1, 0x2d, 0x23, 0x28, 0xa4, 0x00, 0x23, 0x2a, 0xb4, 0x00, 0x23, 0x2c, 0xc4, + 0x00, 0x23, 0x2e, 0xd4, 0x00, 0x03, 0x25, 0x01, 0x2c, 0x83, 0x25, 0x41, 0x2c, 0x03, 0x26, 0x81, 0x2c, 0x83, 0x26, + 0xc1, 0x2c, 0x23, 0x20, 0xa4, 0x00, 0x23, 0x22, 0xb4, 0x00, 0x23, 0x24, 0xc4, 0x00, 0x23, 0x26, 0xd4, 0x00, 0x03, + 0x25, 0x01, 0x2a, 0x83, 0x25, 0x41, 0x2a, 0x03, 0x26, 0x81, 0x2a, 0x83, 0x26, 0xc1, 0x2a, 0x23, 0xa0, 0xab, 0x00, + 0x23, 0xa2, 0xbb, 0x00, 0x23, 0xa4, 0xcb, 0x00, 0x23, 0xa6, 0xdb, 0x00, 0x03, 0x25, 0x01, 0x2b, 0x83, 0x25, 0x41, + 0x2b, 0x03, 0x26, 0x81, 0x2b, 0x83, 0x26, 0xc1, 0x2b, 0x23, 0xa8, 0xab, 0x00, 0x23, 0xaa, 0xbb, 0x00, 0x23, 0xac, + 0xcb, 0x00, 0x23, 0xae, 0xdb, 0x00, 0x13, 0x05, 0x01, 0x79, 0x93, 0x05, 0x01, 0x2c, 0x93, 0x06, 0xc1, 0x13, 0x13, + 0x86, 0x09, 0x00, 0x03, 0x27, 0x01, 0x12, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0x50, 0x03, 0x25, 0x41, 0x79, + 0x83, 0x25, 0x01, 0x79, 0x33, 0x45, 0xb5, 0x01, 0xb3, 0xc5, 0xa5, 0x01, 0x33, 0xe5, 0xa5, 0x00, 0x63, 0x04, 0x05, + 0x00, 0x6f, 0x20, 0x40, 0x35, 0x03, 0x25, 0x81, 0x79, 0x83, 0x25, 0xc1, 0x79, 0x03, 0x26, 0x81, 0x13, 0x33, 0x45, + 0xc5, 0x00, 0x03, 0x26, 0x41, 0x13, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, 0x6f, + 0x20, 0x00, 0x33, 0x03, 0x25, 0x01, 0x7a, 0x83, 0x25, 0x41, 0x7a, 0x03, 0x26, 0x01, 0x13, 0x33, 0x45, 0xc5, 0x00, + 0x03, 0x26, 0xc1, 0x12, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, 0x6f, 0x20, 0xc0, + 0x30, 0x03, 0x25, 0x81, 0x7a, 0x83, 0x25, 0xc1, 0x7a, 0x03, 0x26, 0x81, 0x12, 0x33, 0x45, 0xc5, 0x00, 0x03, 0x26, + 0x41, 0x12, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, 0x6f, 0x20, 0x80, 0x2e, 0x33, + 0xb5, 0x84, 0x03, 0xb3, 0x85, 0x54, 0x03, 0x33, 0x0a, 0x49, 0x01, 0xb3, 0x0a, 0x89, 0x03, 0x33, 0x8c, 0x84, 0x03, + 0x33, 0x8b, 0x64, 0x01, 0x93, 0x8b, 0x0b, 0x02, 0x93, 0x8c, 0xfc, 0xff, 0x33, 0x05, 0xb5, 0x00, 0xb3, 0x35, 0x9b, + 0x00, 0xb3, 0x0a, 0x55, 0x01, 0x33, 0x0a, 0xba, 0x00, 0x13, 0x04, 0x04, 0x02, 0xe3, 0x96, 0x0c, 0xde, 0x03, 0x24, + 0xc1, 0x13, 0x13, 0x05, 0x01, 0x2e, 0x13, 0x06, 0x00, 0x08, 0x93, 0x05, 0x00, 0x00, 0x97, 0x70, 0x00, 0x00, 0xe7, + 0x80, 0x40, 0xdc, 0x13, 0x03, 0x00, 0x00, 0x03, 0x25, 0x01, 0x14, 0x23, 0x24, 0xa1, 0x10, 0x03, 0x25, 0x41, 0x14, + 0x23, 0x22, 0xa1, 0x10, 0x83, 0x29, 0x81, 0x14, 0x03, 0x25, 0xc1, 0x14, 0x23, 0x20, 0xa1, 0x10, 0x03, 0x25, 0x01, + 0x15, 0x23, 0x2e, 0xa1, 0x0e, 0x03, 0x25, 0x41, 0x15, 0x23, 0x2c, 0xa1, 0x0e, 0x03, 0x25, 0x81, 0x15, 0x23, 0x26, + 0xa1, 0x0e, 0x03, 0x25, 0xc1, 0x15, 0x23, 0x2e, 0xa1, 0x0c, 0x93, 0x04, 0x01, 0x2f, 0x13, 0x09, 0x01, 0x1b, 0xb7, + 0xb5, 0x02, 0x00, 0x37, 0xbe, 0x02, 0x00, 0x37, 0x05, 0x02, 0x00, 0x93, 0x03, 0x85, 0xf9, 0xb7, 0xbb, 0x00, 0x00, + 0x93, 0x8b, 0x0b, 0x91, 0x93, 0x0d, 0x04, 0x00, 0x83, 0x2e, 0x04, 0x00, 0x03, 0x2f, 0x44, 0x00, 0x83, 0x2f, 0x84, + 0x00, 0x03, 0x24, 0xc4, 0x00, 0x83, 0xac, 0x0d, 0x01, 0x03, 0xad, 0x4d, 0x01, 0x83, 0xa0, 0x8d, 0x01, 0x03, 0xa5, + 0xcd, 0x01, 0x23, 0x20, 0xa1, 0x12, 0x83, 0x22, 0x09, 0xff, 0x83, 0x28, 0x49, 0xff, 0x03, 0x28, 0x89, 0xff, 0x83, + 0x27, 0xc9, 0xff, 0x03, 0x27, 0x09, 0x00, 0x83, 0x26, 0x49, 0x00, 0x03, 0x26, 0x89, 0x00, 0x03, 0x25, 0xc9, 0x00, + 0x03, 0xc0, 0x55, 0x91, 0x83, 0x25, 0x0e, 0x91, 0xe3, 0xe0, 0xb3, 0x92, 0x23, 0x2c, 0x71, 0x0c, 0x23, 0x2a, 0x61, + 0x0c, 0x13, 0x83, 0x85, 0x06, 0xb3, 0x85, 0xbb, 0x00, 0x83, 0x2b, 0x81, 0x10, 0x93, 0xd3, 0x0b, 0x01, 0x23, 0x28, + 0x6e, 0x90, 0x13, 0xd3, 0x8b, 0x00, 0xa3, 0x84, 0x65, 0x00, 0x13, 0xd3, 0x8b, 0x01, 0x23, 0x85, 0x75, 0x00, 0x03, + 0x2e, 0x41, 0x10, 0x93, 0x53, 0x0e, 0x01, 0xa3, 0x85, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x00, 0xa3, 0x86, 0x65, 0x00, + 0x13, 0x53, 0x8e, 0x01, 0x23, 0x87, 0x75, 0x00, 0x93, 0xd3, 0x09, 0x01, 0xa3, 0x87, 0x65, 0x00, 0x13, 0xd3, 0x89, + 0x00, 0xa3, 0x88, 0x65, 0x00, 0x13, 0xd3, 0x89, 0x01, 0x23, 0x89, 0x75, 0x00, 0x03, 0x2e, 0x01, 0x10, 0x93, 0x53, + 0x0e, 0x01, 0xa3, 0x89, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x00, 0xa3, 0x8a, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x01, 0x23, + 0x8b, 0x75, 0x00, 0x03, 0x2e, 0xc1, 0x0f, 0x93, 0x53, 0x0e, 0x01, 0xa3, 0x8b, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x00, + 0xa3, 0x8c, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x01, 0x23, 0x8d, 0x75, 0x00, 0x03, 0x2e, 0x81, 0x0f, 0x93, 0x53, 0x0e, + 0x01, 0xa3, 0x8d, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x00, 0xa3, 0x8e, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x01, 0x23, 0x8f, + 0x75, 0x00, 0x03, 0x2e, 0xc1, 0x0e, 0x93, 0x53, 0x0e, 0x01, 0xa3, 0x8f, 0x65, 0x00, 0x13, 0x53, 0x8e, 0x00, 0xa3, + 0x80, 0x65, 0x02, 0x13, 0x53, 0x8e, 0x01, 0x23, 0x81, 0x75, 0x02, 0x83, 0x2b, 0xc1, 0x0d, 0x93, 0xd3, 0x0b, 0x01, + 0xa3, 0x81, 0x65, 0x02, 0x13, 0xd3, 0x8b, 0x00, 0xa3, 0x82, 0x65, 0x02, 0x13, 0xd3, 0x8b, 0x01, 0x23, 0x83, 0x75, + 0x02, 0x23, 0x28, 0x81, 0x10, 0x03, 0x24, 0xc1, 0x0a, 0x93, 0x53, 0x04, 0x01, 0xa3, 0x83, 0x65, 0x02, 0x13, 0x53, + 0x84, 0x00, 0xa3, 0x84, 0x65, 0x02, 0x13, 0x53, 0x84, 0x01, 0x23, 0x85, 0x75, 0x02, 0x23, 0x26, 0x91, 0x11, 0x83, + 0x2c, 0x81, 0x0a, 0x93, 0xd3, 0x0c, 0x01, 0xa3, 0x85, 0x65, 0x02, 0x13, 0xd3, 0x8c, 0x00, 0xa3, 0x86, 0x65, 0x02, + 0x13, 0xd3, 0x8c, 0x01, 0x23, 0x87, 0x75, 0x02, 0x23, 0x2a, 0xa1, 0x0f, 0x03, 0x2d, 0x41, 0x0a, 0x93, 0x53, 0x0d, + 0x01, 0xa3, 0x87, 0x65, 0x02, 0x13, 0x53, 0x8d, 0x00, 0xa3, 0x88, 0x65, 0x02, 0x13, 0x53, 0x8d, 0x01, 0x23, 0x89, + 0x75, 0x02, 0x03, 0x2e, 0x01, 0x0a, 0x93, 0x53, 0x0e, 0x01, 0xa3, 0x89, 0x65, 0x02, 0x13, 0x53, 0x8e, 0x00, 0xa3, + 0x8a, 0x65, 0x02, 0x13, 0x53, 0x8e, 0x01, 0x23, 0x8b, 0x75, 0x02, 0x23, 0x2c, 0xe1, 0x11, 0x03, 0x2f, 0xc1, 0x09, + 0x93, 0x53, 0x0f, 0x01, 0xa3, 0x8b, 0x65, 0x02, 0x13, 0x53, 0x8f, 0x00, 0xa3, 0x8c, 0x65, 0x02, 0x13, 0x53, 0x8f, + 0x01, 0x23, 0x8d, 0x75, 0x02, 0x23, 0x2a, 0xf1, 0x11, 0x83, 0x2f, 0x81, 0x09, 0x93, 0xd3, 0x0f, 0x01, 0xa3, 0x8d, + 0x65, 0x02, 0x13, 0xd3, 0x8f, 0x00, 0xa3, 0x8e, 0x65, 0x02, 0x13, 0xd3, 0x8f, 0x01, 0x23, 0x8f, 0x75, 0x02, 0x23, + 0x28, 0x11, 0x0e, 0x83, 0x20, 0x41, 0x09, 0x93, 0xd3, 0x00, 0x01, 0xa3, 0x8f, 0x65, 0x02, 0x13, 0xd3, 0x80, 0x00, + 0xa3, 0x80, 0x65, 0x04, 0x13, 0xd3, 0x80, 0x01, 0x23, 0x81, 0x75, 0x04, 0x23, 0x2e, 0xd1, 0x11, 0x83, 0x2e, 0x01, + 0x09, 0x93, 0xd3, 0x0e, 0x01, 0xa3, 0x81, 0x65, 0x04, 0x13, 0xd3, 0x8e, 0x00, 0xa3, 0x82, 0x65, 0x04, 0x13, 0xd3, + 0x8e, 0x01, 0x23, 0x83, 0x75, 0x04, 0x93, 0xd3, 0x02, 0x01, 0xa3, 0x83, 0x65, 0x04, 0x13, 0xd3, 0x82, 0x00, 0xa3, + 0x84, 0x65, 0x04, 0x23, 0x84, 0x55, 0x04, 0x93, 0xd2, 0x82, 0x01, 0x23, 0x85, 0x75, 0x04, 0x13, 0xd3, 0x08, 0x01, + 0xa3, 0x85, 0x55, 0x04, 0x93, 0xd2, 0x88, 0x00, 0xa3, 0x86, 0x55, 0x04, 0x23, 0x86, 0x15, 0x05, 0x93, 0xd8, 0x88, + 0x01, 0x23, 0x87, 0x65, 0x04, 0x93, 0x52, 0x08, 0x01, 0xa3, 0x87, 0x15, 0x05, 0x93, 0x58, 0x88, 0x00, 0xa3, 0x88, + 0x15, 0x05, 0x23, 0x88, 0x05, 0x05, 0x13, 0x58, 0x88, 0x01, 0x23, 0x89, 0x55, 0x04, 0x93, 0xd8, 0x07, 0x01, 0xa3, + 0x89, 0x05, 0x05, 0x13, 0xd8, 0x87, 0x00, 0xa3, 0x8a, 0x05, 0x05, 0x23, 0x8a, 0xf5, 0x04, 0x93, 0xd7, 0x87, 0x01, + 0x23, 0x8b, 0x15, 0x05, 0x13, 0x58, 0x07, 0x01, 0xa3, 0x8b, 0xf5, 0x04, 0x93, 0x57, 0x87, 0x00, 0xa3, 0x8c, 0xf5, + 0x04, 0x23, 0x8c, 0xe5, 0x04, 0x13, 0x57, 0x87, 0x01, 0x23, 0x8d, 0x05, 0x05, 0x93, 0xd7, 0x06, 0x01, 0xa3, 0x8d, + 0xe5, 0x04, 0x13, 0xd7, 0x86, 0x00, 0xa3, 0x8e, 0xe5, 0x04, 0x23, 0x8e, 0xd5, 0x04, 0x93, 0xd6, 0x86, 0x01, 0x23, + 0x8f, 0xf5, 0x04, 0x13, 0x57, 0x06, 0x01, 0xa3, 0x8f, 0xd5, 0x04, 0x93, 0x56, 0x86, 0x00, 0xa3, 0x80, 0xd5, 0x06, + 0x23, 0x80, 0xc5, 0x06, 0x13, 0x56, 0x86, 0x01, 0x23, 0x81, 0xe5, 0x06, 0x93, 0x56, 0x05, 0x01, 0xa3, 0x81, 0xc5, + 0x06, 0x13, 0x56, 0x85, 0x00, 0xa3, 0x82, 0xc5, 0x06, 0x23, 0x82, 0xa5, 0x06, 0x13, 0x55, 0x85, 0x01, 0x23, 0x83, + 0xd5, 0x06, 0xa3, 0x83, 0xa5, 0x06, 0x03, 0x25, 0x41, 0x10, 0x23, 0x86, 0xa5, 0x00, 0x03, 0x25, 0x81, 0x10, 0x23, + 0x84, 0xa5, 0x00, 0x03, 0x25, 0x01, 0x10, 0x23, 0x8a, 0xa5, 0x00, 0x23, 0x24, 0x31, 0x0f, 0x23, 0x88, 0x35, 0x01, + 0x03, 0x25, 0x81, 0x0f, 0x23, 0x8e, 0xa5, 0x00, 0x03, 0x25, 0xc1, 0x0f, 0x23, 0x8c, 0xa5, 0x00, 0x23, 0x82, 0x75, + 0x03, 0x03, 0x25, 0xc1, 0x0e, 0x23, 0x80, 0xa5, 0x02, 0x23, 0x86, 0x95, 0x03, 0x83, 0x29, 0xc1, 0x10, 0x13, 0x05, + 0x30, 0x00, 0x23, 0x84, 0x85, 0x02, 0x03, 0x24, 0x01, 0x11, 0x23, 0x8a, 0xc5, 0x03, 0x23, 0x88, 0xa5, 0x03, 0x83, + 0x2b, 0x41, 0x0f, 0x23, 0x8e, 0xf5, 0x03, 0x23, 0x8c, 0xe5, 0x03, 0x23, 0x82, 0xd5, 0x05, 0x23, 0x80, 0x15, 0x04, + 0x23, 0x82, 0x05, 0x00, 0xa3, 0x82, 0x05, 0x00, 0x23, 0x83, 0x05, 0x00, 0xa3, 0x83, 0x05, 0x00, 0x23, 0x80, 0xa5, + 0x00, 0xa3, 0x80, 0x05, 0x00, 0x23, 0x81, 0x05, 0x00, 0xa3, 0x81, 0x05, 0x00, 0x13, 0x05, 0x01, 0x79, 0x13, 0x06, + 0x80, 0x06, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xb6, 0x83, 0x27, 0x01, 0x0f, 0x03, 0x27, 0x41, 0x11, 0x83, + 0x26, 0x81, 0x11, 0x03, 0x26, 0xc1, 0x11, 0x03, 0x25, 0x41, 0x79, 0x83, 0x25, 0x01, 0x79, 0x33, 0x45, 0xd5, 0x00, + 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0xe5, 0xa5, 0x00, 0x03, 0x28, 0x01, 0x12, 0x63, 0x04, 0x05, 0x00, 0x6f, 0x10, 0x90, + 0x7a, 0x03, 0x25, 0x81, 0x79, 0x83, 0x25, 0xc1, 0x79, 0x33, 0x45, 0xe5, 0x00, 0xb3, 0xc5, 0x85, 0x00, 0x33, 0x65, + 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, 0x6f, 0x10, 0xd0, 0x78, 0x03, 0x25, 0x01, 0x7a, 0x83, 0x25, 0x41, 0x7a, 0x33, + 0x45, 0x35, 0x01, 0xb3, 0xc5, 0x75, 0x01, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, 0x6f, 0x10, 0x10, 0x77, + 0x03, 0x25, 0x81, 0x7a, 0x83, 0x25, 0xc1, 0x7a, 0x33, 0x45, 0xf5, 0x00, 0xb3, 0xc5, 0x05, 0x01, 0x33, 0x65, 0xb5, + 0x00, 0x63, 0x04, 0x05, 0x00, 0x6f, 0x10, 0x50, 0x75, 0x13, 0x85, 0xcd, 0x01, 0x23, 0xa8, 0xc4, 0xfe, 0x23, 0xaa, + 0xd4, 0xfe, 0x23, 0xac, 0xe4, 0xfe, 0x23, 0xae, 0x84, 0xfe, 0x23, 0xa0, 0x34, 0x01, 0x23, 0xa2, 0x74, 0x01, 0x23, + 0xa4, 0xf4, 0x00, 0x23, 0xa6, 0x04, 0x01, 0x03, 0x23, 0x41, 0x0d, 0x13, 0x03, 0x13, 0x00, 0x93, 0x84, 0x04, 0x02, + 0x13, 0x09, 0x09, 0x02, 0x13, 0x04, 0x45, 0x00, 0x03, 0x25, 0x01, 0x08, 0x83, 0x29, 0x81, 0x0e, 0xb7, 0xb5, 0x02, + 0x00, 0x37, 0xbe, 0x02, 0x00, 0x83, 0x23, 0x81, 0x0d, 0xb7, 0xbb, 0x00, 0x00, 0x93, 0x8b, 0x0b, 0x91, 0xe3, 0x16, + 0x65, 0xb6, 0x93, 0x05, 0x10, 0x00, 0x13, 0x85, 0x0d, 0x02, 0x03, 0x2f, 0x01, 0x08, 0x63, 0x04, 0xbf, 0x00, 0x6f, + 0x10, 0x10, 0x5e, 0x83, 0x24, 0x81, 0x10, 0x03, 0x29, 0x41, 0x10, 0x6f, 0x00, 0xc0, 0x02, 0x13, 0x05, 0x01, 0x2e, + 0x13, 0x06, 0x00, 0x08, 0x93, 0x05, 0x00, 0x00, 0x97, 0x70, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x89, 0x13, 0x0b, 0x00, + 0x00, 0x13, 0x0a, 0x00, 0x00, 0x93, 0x0a, 0x00, 0x00, 0x13, 0x0c, 0x10, 0x00, 0x13, 0x05, 0xc0, 0x18, 0x83, 0x2b, + 0x05, 0x00, 0x03, 0x24, 0x45, 0x00, 0x03, 0x27, 0x85, 0x00, 0x83, 0x26, 0xc5, 0x00, 0x03, 0x28, 0x05, 0x01, 0x83, + 0x27, 0x45, 0x01, 0x83, 0x22, 0x85, 0x01, 0x83, 0x28, 0xc5, 0x01, 0x83, 0x23, 0x05, 0x02, 0x03, 0x23, 0x45, 0x02, + 0x03, 0x26, 0x85, 0x02, 0x93, 0x05, 0x30, 0x00, 0x63, 0x64, 0xb6, 0x00, 0x6f, 0x10, 0x10, 0x6d, 0xb3, 0xe5, 0x8b, + 0x00, 0x23, 0x20, 0xc1, 0x12, 0x23, 0x2a, 0xb1, 0x0e, 0x23, 0x24, 0x91, 0x10, 0x23, 0x22, 0x21, 0x11, 0x23, 0x2c, + 0xd1, 0x0c, 0x23, 0x2a, 0xe1, 0x0c, 0x63, 0x86, 0x05, 0x04, 0x93, 0x05, 0x20, 0x00, 0x63, 0x14, 0xb6, 0x00, 0x6f, + 0x10, 0x10, 0x6c, 0xb3, 0x65, 0xd7, 0x00, 0x63, 0x98, 0x05, 0x06, 0xb3, 0x65, 0xf8, 0x00, 0x63, 0x94, 0x05, 0x06, + 0xb3, 0xe5, 0x12, 0x01, 0x63, 0x90, 0x05, 0x06, 0xb3, 0xe5, 0x63, 0x00, 0x63, 0x9c, 0x05, 0x04, 0x37, 0xa5, 0x00, + 0x00, 0x13, 0x05, 0x45, 0x0f, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, 0x12, 0x93, 0x05, 0x80, 0x03, 0x97, 0x60, + 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xc6, 0x63, 0x14, 0x06, 0x00, 0x6f, 0x10, 0x10, 0x6d, 0xb3, 0x65, 0xd7, 0x00, 0x63, + 0x84, 0x05, 0x00, 0x6f, 0x10, 0x90, 0x63, 0xb3, 0x65, 0xf8, 0x00, 0x63, 0x84, 0x05, 0x00, 0x6f, 0x10, 0xd0, 0x62, + 0xb3, 0xe5, 0x12, 0x01, 0x63, 0x84, 0x05, 0x00, 0x6f, 0x10, 0x10, 0x62, 0xb3, 0xe5, 0x63, 0x00, 0x63, 0x84, 0x05, + 0x00, 0x6f, 0x10, 0x50, 0x61, 0x23, 0x2e, 0x71, 0x0a, 0x23, 0x20, 0x61, 0x0c, 0x23, 0x22, 0x51, 0x0c, 0x23, 0x24, + 0x11, 0x0d, 0x23, 0x26, 0x01, 0x0d, 0x23, 0x28, 0xf1, 0x0c, 0x93, 0x0d, 0xc5, 0x02, 0x23, 0x20, 0x01, 0x36, 0x23, + 0x22, 0x01, 0x36, 0x23, 0x24, 0x01, 0x36, 0x23, 0x26, 0x01, 0x36, 0x13, 0x05, 0x01, 0x37, 0x13, 0x06, 0x00, 0x04, + 0x93, 0x0c, 0x01, 0x37, 0x93, 0x05, 0x00, 0x00, 0x97, 0x60, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x76, 0x13, 0x05, 0x01, + 0x3b, 0x13, 0x06, 0x00, 0x04, 0x13, 0x09, 0x01, 0x3b, 0x93, 0x05, 0x00, 0x00, 0x97, 0x60, 0x00, 0x00, 0xe7, 0x80, + 0x00, 0x75, 0x13, 0x05, 0x01, 0x3f, 0x13, 0x06, 0x00, 0x04, 0x93, 0x09, 0x01, 0x3f, 0x93, 0x05, 0x00, 0x00, 0x97, + 0x60, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x73, 0x83, 0x25, 0x01, 0x12, 0x23, 0x28, 0x81, 0x0e, 0x63, 0x8e, 0x05, 0x68, + 0x23, 0x26, 0x71, 0x0b, 0x13, 0x0d, 0x00, 0x00, 0x93, 0x04, 0x00, 0x00, 0x93, 0x00, 0x01, 0x36, 0x37, 0x05, 0x02, + 0x00, 0x13, 0x05, 0x95, 0xf9, 0x23, 0x2e, 0xa1, 0x10, 0x13, 0x87, 0x05, 0x00, 0x23, 0x28, 0xe1, 0x10, 0x23, 0x2a, + 0x91, 0x11, 0x23, 0x2c, 0x91, 0x10, 0x13, 0x84, 0x0d, 0x00, 0x83, 0xad, 0x0d, 0x00, 0x03, 0x25, 0x44, 0x00, 0x23, + 0xa0, 0xb0, 0x01, 0x23, 0x26, 0xa1, 0x10, 0x23, 0xa2, 0xa0, 0x00, 0x03, 0x25, 0x84, 0x00, 0x83, 0x25, 0xc4, 0x00, + 0x03, 0x26, 0x04, 0x01, 0x83, 0x26, 0x44, 0x01, 0x03, 0x27, 0x84, 0x01, 0x83, 0x27, 0xc4, 0x01, 0x03, 0x28, 0x04, + 0x02, 0x83, 0x28, 0x44, 0x02, 0x23, 0x28, 0xa1, 0x42, 0x23, 0x2a, 0xb1, 0x42, 0x23, 0x2c, 0xc1, 0x42, 0x23, 0x2e, + 0xd1, 0x42, 0x23, 0x20, 0xe1, 0x44, 0x23, 0x22, 0xf1, 0x44, 0x23, 0x24, 0x01, 0x45, 0x23, 0x26, 0x11, 0x45, 0x83, + 0x2b, 0x84, 0x02, 0x83, 0x2c, 0xc4, 0x02, 0x03, 0x2f, 0x04, 0x03, 0x83, 0x2f, 0x44, 0x03, 0x03, 0x2e, 0x84, 0x03, + 0x83, 0x2e, 0xc4, 0x03, 0x03, 0x23, 0x04, 0x04, 0x83, 0x23, 0x44, 0x04, 0x83, 0x28, 0x84, 0x04, 0x83, 0x22, 0xc4, + 0x04, 0x83, 0x27, 0x04, 0x05, 0x03, 0x28, 0x44, 0x05, 0x83, 0x26, 0x84, 0x05, 0x03, 0x27, 0xc4, 0x05, 0x03, 0x25, + 0x04, 0x06, 0x03, 0x26, 0x44, 0x06, 0xb7, 0xb5, 0x02, 0x00, 0x03, 0xc0, 0x55, 0x91, 0xb7, 0xb5, 0x02, 0x00, 0x83, + 0xa4, 0x05, 0x91, 0x83, 0x25, 0xc1, 0x11, 0x63, 0xf0, 0xb4, 0xa6, 0x23, 0x2c, 0xa1, 0x0b, 0x33, 0xbd, 0x8d, 0x03, + 0xb3, 0x8a, 0x5d, 0x03, 0x23, 0x2a, 0x11, 0x0a, 0x93, 0x80, 0x84, 0x06, 0xb7, 0xb5, 0x00, 0x00, 0x93, 0x85, 0x05, + 0x91, 0xb3, 0x85, 0x95, 0x00, 0xb3, 0x0a, 0x5d, 0x01, 0x23, 0x28, 0x51, 0x0b, 0x83, 0x2a, 0x81, 0x10, 0x13, 0xdd, + 0x0a, 0x01, 0xb7, 0xb4, 0x02, 0x00, 0x23, 0xa8, 0x14, 0x90, 0x93, 0xd0, 0x8a, 0x00, 0xa3, 0x84, 0x15, 0x00, 0x93, + 0xd0, 0x8a, 0x01, 0x23, 0x85, 0xa5, 0x01, 0x83, 0x24, 0x41, 0x10, 0x13, 0xdd, 0x04, 0x01, 0xa3, 0x85, 0x15, 0x00, + 0x93, 0xd0, 0x84, 0x00, 0xa3, 0x86, 0x15, 0x00, 0x93, 0xd0, 0x84, 0x01, 0x23, 0x87, 0xa5, 0x01, 0x83, 0x24, 0x81, + 0x0e, 0x13, 0xdd, 0x04, 0x01, 0xa3, 0x87, 0x15, 0x00, 0x93, 0xd0, 0x84, 0x00, 0xa3, 0x88, 0x15, 0x00, 0x93, 0xd0, + 0x84, 0x01, 0x23, 0x89, 0xa5, 0x01, 0x83, 0x24, 0x01, 0x10, 0x13, 0xdd, 0x04, 0x01, 0xa3, 0x89, 0x15, 0x00, 0x93, + 0xd0, 0x84, 0x00, 0xa3, 0x8a, 0x15, 0x00, 0x93, 0xd0, 0x84, 0x01, 0x23, 0x8b, 0xa5, 0x01, 0x83, 0x24, 0xc1, 0x0f, + 0x13, 0xdd, 0x04, 0x01, 0xa3, 0x8b, 0x15, 0x00, 0x93, 0xd0, 0x84, 0x00, 0xa3, 0x8c, 0x15, 0x00, 0x93, 0xd0, 0x84, + 0x01, 0x23, 0x8d, 0xa5, 0x01, 0x83, 0x24, 0x81, 0x0f, 0x13, 0xdd, 0x04, 0x01, 0xa3, 0x8d, 0x15, 0x00, 0x93, 0xd0, + 0x84, 0x00, 0xa3, 0x8e, 0x15, 0x00, 0x93, 0xd0, 0x84, 0x01, 0x23, 0x8f, 0xa5, 0x01, 0x83, 0x2a, 0xc1, 0x0e, 0x13, + 0xdd, 0x0a, 0x01, 0xa3, 0x8f, 0x15, 0x00, 0x93, 0xd0, 0x8a, 0x00, 0xa3, 0x80, 0x15, 0x02, 0x93, 0xd0, 0x8a, 0x01, + 0x23, 0x81, 0xa5, 0x03, 0x83, 0x24, 0xc1, 0x0d, 0x13, 0xdd, 0x04, 0x01, 0xa3, 0x81, 0x15, 0x02, 0x93, 0xd0, 0x84, + 0x00, 0xa3, 0x82, 0x15, 0x02, 0x93, 0xd0, 0x84, 0x01, 0x23, 0x83, 0xa5, 0x03, 0x13, 0xdd, 0x0c, 0x01, 0xa3, 0x83, + 0x15, 0x02, 0x93, 0xd0, 0x8c, 0x00, 0xa3, 0x86, 0x15, 0x02, 0x23, 0x86, 0x95, 0x03, 0x93, 0xdc, 0x8c, 0x01, 0x23, + 0x87, 0xa5, 0x03, 0x13, 0xdd, 0x0b, 0x01, 0xa3, 0x87, 0x95, 0x03, 0x93, 0xdc, 0x8b, 0x00, 0xa3, 0x84, 0x95, 0x03, + 0x23, 0x84, 0x75, 0x03, 0x93, 0xdb, 0x8b, 0x01, 0x23, 0x85, 0xa5, 0x03, 0x03, 0x2d, 0x81, 0x0b, 0x93, 0xdc, 0x0f, + 0x01, 0xa3, 0x85, 0x75, 0x03, 0x93, 0xdb, 0x8f, 0x00, 0xa3, 0x8a, 0x75, 0x03, 0x23, 0x8a, 0xf5, 0x03, 0x93, 0xdf, + 0x8f, 0x01, 0x23, 0x8b, 0x95, 0x03, 0x93, 0x5b, 0x0f, 0x01, 0xa3, 0x8b, 0xf5, 0x03, 0x93, 0x5f, 0x8f, 0x00, 0xa3, + 0x88, 0xf5, 0x03, 0x23, 0x88, 0xe5, 0x03, 0x13, 0x5f, 0x8f, 0x01, 0x23, 0x89, 0x75, 0x03, 0x93, 0xdf, 0x0e, 0x01, + 0xa3, 0x89, 0xe5, 0x03, 0x13, 0xdf, 0x8e, 0x00, 0xa3, 0x8e, 0xe5, 0x03, 0x23, 0x8e, 0xd5, 0x03, 0x93, 0xde, 0x8e, + 0x01, 0x23, 0x8f, 0xf5, 0x03, 0x13, 0x5f, 0x0e, 0x01, 0xa3, 0x8f, 0xd5, 0x03, 0x93, 0x5e, 0x8e, 0x00, 0xa3, 0x8c, + 0xd5, 0x03, 0x23, 0x8c, 0xc5, 0x03, 0x13, 0x5e, 0x8e, 0x01, 0x23, 0x8d, 0xe5, 0x03, 0x93, 0xde, 0x03, 0x01, 0xa3, + 0x8d, 0xc5, 0x03, 0x13, 0xde, 0x83, 0x00, 0xa3, 0x82, 0xc5, 0x05, 0x23, 0x82, 0x75, 0x04, 0x93, 0xd3, 0x83, 0x01, + 0x23, 0x83, 0xd5, 0x05, 0x13, 0x5e, 0x03, 0x01, 0xa3, 0x83, 0x75, 0x04, 0x93, 0x53, 0x83, 0x00, 0xa3, 0x80, 0x75, + 0x04, 0x23, 0x80, 0x65, 0x04, 0x13, 0x53, 0x83, 0x01, 0x23, 0x81, 0xc5, 0x05, 0x93, 0xd3, 0x02, 0x01, 0xa3, 0x81, + 0x65, 0x04, 0x13, 0xd3, 0x82, 0x00, 0xa3, 0x86, 0x65, 0x04, 0x23, 0x86, 0x55, 0x04, 0x93, 0xd2, 0x82, 0x01, 0x23, + 0x87, 0x75, 0x04, 0x13, 0xd3, 0x08, 0x01, 0xa3, 0x87, 0x55, 0x04, 0x93, 0xd2, 0x88, 0x00, 0xa3, 0x84, 0x55, 0x04, + 0x23, 0x84, 0x15, 0x05, 0x93, 0xd8, 0x88, 0x01, 0x23, 0x85, 0x65, 0x04, 0x93, 0x52, 0x08, 0x01, 0xa3, 0x85, 0x15, + 0x05, 0x93, 0x58, 0x88, 0x00, 0xa3, 0x8a, 0x15, 0x05, 0x23, 0x8a, 0x05, 0x05, 0x13, 0x58, 0x88, 0x01, 0x23, 0x8b, + 0x55, 0x04, 0x93, 0xd8, 0x07, 0x01, 0xa3, 0x8b, 0x05, 0x05, 0x13, 0xd8, 0x87, 0x00, 0xa3, 0x88, 0x05, 0x05, 0x23, + 0x88, 0xf5, 0x04, 0x93, 0xd7, 0x87, 0x01, 0x23, 0x89, 0x15, 0x05, 0x13, 0x58, 0x07, 0x01, 0xa3, 0x89, 0xf5, 0x04, + 0x93, 0x57, 0x87, 0x00, 0xa3, 0x8e, 0xf5, 0x04, 0x23, 0x8e, 0xe5, 0x04, 0x13, 0x57, 0x87, 0x01, 0x23, 0x8f, 0x05, + 0x05, 0x93, 0xd7, 0x06, 0x01, 0xa3, 0x8f, 0xe5, 0x04, 0x13, 0xd7, 0x86, 0x00, 0xa3, 0x8c, 0xe5, 0x04, 0x23, 0x8c, + 0xd5, 0x04, 0x93, 0xd6, 0x86, 0x01, 0x23, 0x8d, 0xf5, 0x04, 0x13, 0x57, 0x06, 0x01, 0xa3, 0x8d, 0xd5, 0x04, 0x93, + 0x56, 0x86, 0x00, 0xa3, 0x82, 0xd5, 0x06, 0x23, 0x82, 0xc5, 0x06, 0x13, 0x56, 0x86, 0x01, 0x23, 0x83, 0xe5, 0x06, + 0x93, 0x56, 0x05, 0x01, 0xa3, 0x83, 0xc5, 0x06, 0x13, 0x56, 0x85, 0x00, 0xa3, 0x80, 0xc5, 0x06, 0x23, 0x80, 0xa5, + 0x06, 0x13, 0x55, 0x85, 0x01, 0x23, 0x81, 0xd5, 0x06, 0x03, 0x26, 0x41, 0x10, 0x23, 0x86, 0xc5, 0x00, 0x03, 0x26, + 0x81, 0x10, 0x23, 0x84, 0xc5, 0x00, 0x03, 0x26, 0x01, 0x10, 0x23, 0x8a, 0xc5, 0x00, 0x03, 0x26, 0x81, 0x0e, 0x23, + 0x88, 0xc5, 0x00, 0x03, 0x26, 0x81, 0x0f, 0x23, 0x8e, 0xc5, 0x00, 0x03, 0x26, 0xc1, 0x0f, 0x23, 0x8c, 0xc5, 0x00, + 0x23, 0x82, 0x95, 0x02, 0x23, 0x80, 0x55, 0x03, 0xa3, 0x81, 0xa5, 0x06, 0x83, 0x2c, 0xc1, 0x10, 0x33, 0x85, 0x8c, + 0x03, 0x33, 0x8d, 0xad, 0x01, 0x03, 0x26, 0x81, 0x11, 0x33, 0x86, 0xcc, 0x00, 0x83, 0x2a, 0x01, 0x0b, 0xb3, 0x8a, + 0xaa, 0x00, 0x33, 0x35, 0xbd, 0x01, 0xb3, 0x04, 0xa6, 0x00, 0x93, 0x0b, 0x44, 0x06, 0x33, 0x8c, 0x8d, 0x03, 0x23, + 0x82, 0x05, 0x00, 0xa3, 0x82, 0x05, 0x00, 0x23, 0x83, 0x05, 0x00, 0xa3, 0x83, 0x05, 0x00, 0x13, 0x05, 0x50, 0x00, + 0x23, 0x80, 0xa5, 0x00, 0xa3, 0x80, 0x05, 0x00, 0x23, 0x81, 0x05, 0x00, 0xa3, 0x81, 0x05, 0x00, 0x13, 0x05, 0x01, + 0x79, 0x13, 0x06, 0x80, 0x06, 0x97, 0x20, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x49, 0x03, 0x25, 0x01, 0x79, 0x83, 0x25, + 0x41, 0x79, 0x03, 0x26, 0x81, 0x79, 0x83, 0x26, 0xc1, 0x79, 0x03, 0x27, 0x01, 0x7a, 0x83, 0x27, 0x41, 0x7a, 0x03, + 0x28, 0x81, 0x7a, 0x83, 0x28, 0xc1, 0x7a, 0x23, 0x28, 0xa1, 0x44, 0x23, 0x2a, 0xb1, 0x44, 0x23, 0x2c, 0xc1, 0x44, + 0x23, 0x2e, 0xd1, 0x44, 0x23, 0x20, 0xe1, 0x46, 0x23, 0x22, 0xf1, 0x46, 0x23, 0x24, 0x01, 0x47, 0x23, 0x26, 0x11, + 0x47, 0x13, 0x05, 0x01, 0x47, 0x93, 0x05, 0x01, 0x14, 0x13, 0x07, 0x01, 0x43, 0x93, 0x07, 0x01, 0x45, 0x13, 0x08, + 0x01, 0x18, 0x13, 0x86, 0x0d, 0x00, 0x93, 0x86, 0x0c, 0x00, 0x97, 0xc0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0x65, 0x83, + 0x20, 0x41, 0x0b, 0x03, 0x25, 0x01, 0x44, 0x83, 0x25, 0x41, 0x44, 0x03, 0x26, 0x81, 0x44, 0x83, 0x26, 0xc1, 0x44, + 0x83, 0x2c, 0x41, 0x11, 0x23, 0xa8, 0xac, 0x00, 0x23, 0xaa, 0xbc, 0x00, 0x23, 0xac, 0xcc, 0x00, 0x23, 0xae, 0xdc, + 0x00, 0x03, 0x25, 0x01, 0x43, 0x83, 0x25, 0x41, 0x43, 0x03, 0x26, 0x81, 0x43, 0x83, 0x26, 0xc1, 0x43, 0x23, 0xa0, + 0xac, 0x00, 0x23, 0xa2, 0xbc, 0x00, 0x23, 0xa4, 0xcc, 0x00, 0x23, 0xa6, 0xdc, 0x00, 0x03, 0x25, 0x01, 0x47, 0x83, + 0x25, 0x41, 0x47, 0x03, 0x26, 0x81, 0x47, 0x83, 0x26, 0xc1, 0x47, 0x23, 0x20, 0xa9, 0x00, 0x23, 0x22, 0xb9, 0x00, + 0x23, 0x24, 0xc9, 0x00, 0x23, 0x26, 0xd9, 0x00, 0x03, 0x25, 0x01, 0x48, 0x83, 0x25, 0x41, 0x48, 0x03, 0x26, 0x81, + 0x48, 0x83, 0x26, 0xc1, 0x48, 0x23, 0x28, 0xa9, 0x00, 0x23, 0x2a, 0xb9, 0x00, 0x23, 0x2c, 0xc9, 0x00, 0x23, 0x2e, + 0xd9, 0x00, 0x03, 0x25, 0x01, 0x46, 0x83, 0x25, 0x41, 0x46, 0x03, 0x26, 0x81, 0x46, 0x83, 0x26, 0xc1, 0x46, 0x23, + 0xa8, 0xa9, 0x00, 0x23, 0xaa, 0xb9, 0x00, 0x23, 0xac, 0xc9, 0x00, 0x23, 0xae, 0xd9, 0x00, 0x03, 0x25, 0x01, 0x45, + 0x83, 0x25, 0x41, 0x45, 0x03, 0x26, 0x81, 0x45, 0x83, 0x26, 0xc1, 0x45, 0x93, 0x80, 0x80, 0x00, 0x03, 0x27, 0x01, + 0x11, 0x13, 0x07, 0xf7, 0xff, 0x23, 0xa0, 0xa9, 0x00, 0x23, 0xa2, 0xb9, 0x00, 0x23, 0xa4, 0xc9, 0x00, 0x23, 0xa6, + 0xd9, 0x00, 0x93, 0x89, 0x09, 0x02, 0x13, 0x09, 0x09, 0x02, 0x93, 0x8c, 0x0c, 0x02, 0x93, 0x8d, 0x4b, 0x00, 0xe3, + 0x18, 0x07, 0xa8, 0x13, 0x04, 0x84, 0x06, 0x13, 0x05, 0x01, 0x49, 0x13, 0x06, 0x00, 0x04, 0x93, 0x05, 0x00, 0x00, + 0x97, 0x60, 0x00, 0x00, 0xe7, 0x80, 0x00, 0x18, 0x13, 0x05, 0x01, 0x3c, 0x93, 0x05, 0x01, 0x4a, 0x03, 0x26, 0x01, + 0x12, 0x83, 0x2f, 0x01, 0x08, 0x83, 0x2b, 0xc1, 0x0a, 0x03, 0x27, 0x04, 0x00, 0x83, 0x27, 0x44, 0x00, 0x03, 0x28, + 0x84, 0x00, 0x83, 0x28, 0xc4, 0x00, 0x83, 0x22, 0x04, 0x01, 0x03, 0x23, 0x44, 0x01, 0x83, 0x23, 0x84, 0x01, 0x03, + 0x2e, 0xc4, 0x01, 0x83, 0x2e, 0x05, 0xff, 0x03, 0x2f, 0x45, 0xff, 0xb3, 0xce, 0xee, 0x00, 0x33, 0x4f, 0xff, 0x00, + 0xb3, 0xee, 0xee, 0x01, 0x63, 0x84, 0x0e, 0x00, 0x6f, 0x10, 0xc0, 0x77, 0x83, 0x2e, 0xc5, 0xff, 0x03, 0x2f, 0x85, + 0xff, 0xb3, 0xce, 0x1e, 0x01, 0x33, 0x4f, 0x0f, 0x01, 0xb3, 0x6e, 0xdf, 0x01, 0x63, 0x84, 0x0e, 0x00, 0x6f, 0x10, + 0x00, 0x76, 0x83, 0x2e, 0x45, 0x00, 0x03, 0x2f, 0x05, 0x00, 0xb3, 0xce, 0x6e, 0x00, 0x33, 0x4f, 0x5f, 0x00, 0xb3, + 0x6e, 0xdf, 0x01, 0x63, 0x84, 0x0e, 0x00, 0x6f, 0x10, 0x40, 0x74, 0x83, 0x2e, 0xc5, 0x00, 0x03, 0x2f, 0x85, 0x00, + 0xb3, 0xce, 0xce, 0x01, 0x33, 0x4f, 0x7f, 0x00, 0xb3, 0x6e, 0xdf, 0x01, 0x63, 0x84, 0x0e, 0x00, 0x6f, 0x10, 0x80, + 0x72, 0x93, 0x06, 0x04, 0x00, 0x13, 0x04, 0xc4, 0x01, 0x23, 0xa8, 0xe5, 0xfe, 0x23, 0xaa, 0xf5, 0xfe, 0x23, 0xac, + 0x05, 0xff, 0x23, 0xae, 0x15, 0xff, 0x23, 0xa0, 0x55, 0x00, 0x23, 0xa2, 0x65, 0x00, 0x23, 0xa4, 0x75, 0x00, 0x23, + 0xa6, 0xc5, 0x01, 0x13, 0x06, 0xf6, 0xff, 0x13, 0x05, 0x05, 0x02, 0x93, 0x85, 0x05, 0x02, 0x13, 0x04, 0x44, 0x00, + 0xe3, 0x1c, 0x06, 0xf2, 0x93, 0x8d, 0x06, 0x02, 0x03, 0x24, 0x01, 0x0f, 0x6f, 0x00, 0x00, 0x02, 0x13, 0x05, 0x01, + 0x49, 0x13, 0x06, 0x00, 0x04, 0x97, 0x60, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x08, 0x13, 0x0d, 0x00, 0x00, 0x93, 0x04, + 0x00, 0x00, 0x83, 0x2f, 0x01, 0x08, 0x33, 0x05, 0x7d, 0x01, 0xb3, 0x85, 0x84, 0x00, 0x33, 0x36, 0xa5, 0x01, 0xb3, + 0x85, 0xc5, 0x00, 0xb3, 0x45, 0xba, 0x00, 0x33, 0x45, 0xab, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, + 0x6f, 0x10, 0x00, 0x71, 0x93, 0x97, 0x5f, 0x00, 0x83, 0x20, 0x01, 0x12, 0x03, 0x24, 0x41, 0x0f, 0x23, 0x2e, 0xf1, + 0x10, 0x63, 0x04, 0x04, 0x0a, 0x13, 0x05, 0x10, 0x00, 0x63, 0x90, 0xa0, 0x0a, 0x03, 0x25, 0x01, 0x3f, 0x83, 0x25, + 0x41, 0x3f, 0x03, 0x26, 0x41, 0x16, 0x83, 0x26, 0x01, 0x16, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, + 0x65, 0xb5, 0x00, 0x63, 0x12, 0x05, 0x06, 0x03, 0x25, 0x81, 0x3f, 0x83, 0x25, 0xc1, 0x3f, 0x03, 0x26, 0xc1, 0x16, + 0x83, 0x26, 0x81, 0x16, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x12, 0x05, + 0x04, 0x03, 0x25, 0x01, 0x40, 0x83, 0x25, 0x41, 0x40, 0x03, 0x26, 0x41, 0x17, 0x83, 0x26, 0x01, 0x17, 0xb3, 0xc5, + 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x12, 0x05, 0x02, 0x03, 0x25, 0x81, 0x40, 0x83, + 0x25, 0xc1, 0x40, 0x03, 0x26, 0xc1, 0x17, 0x83, 0x26, 0x81, 0x17, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, + 0x33, 0x65, 0xb5, 0x00, 0x63, 0x08, 0x05, 0x0c, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0xc5, 0x16, 0x37, 0xa6, 0x00, + 0x00, 0x13, 0x06, 0x46, 0x1a, 0x93, 0x05, 0x80, 0x03, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x42, 0x63, 0x12, + 0x04, 0x0a, 0x13, 0x05, 0x20, 0x00, 0x63, 0x9e, 0xa0, 0x08, 0x03, 0x25, 0x01, 0x41, 0x83, 0x25, 0x41, 0x41, 0x03, + 0x26, 0x41, 0x16, 0x83, 0x26, 0x01, 0x16, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, + 0x63, 0x04, 0x05, 0x00, 0x6f, 0x10, 0xc0, 0x6a, 0x03, 0x25, 0x81, 0x41, 0x83, 0x25, 0xc1, 0x41, 0x03, 0x26, 0xc1, + 0x16, 0x83, 0x26, 0x81, 0x16, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, + 0x05, 0x00, 0x6f, 0x10, 0x80, 0x68, 0x03, 0x25, 0x01, 0x42, 0x83, 0x25, 0x41, 0x42, 0x03, 0x26, 0x41, 0x17, 0x83, + 0x26, 0x01, 0x17, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, + 0x6f, 0x10, 0x40, 0x66, 0x03, 0x25, 0x81, 0x42, 0x83, 0x25, 0xc1, 0x42, 0x03, 0x26, 0xc1, 0x17, 0x83, 0x26, 0x81, + 0x17, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x45, 0xd5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, 0x00, 0x6f, 0x10, + 0x00, 0x64, 0x13, 0x05, 0x10, 0x00, 0x6f, 0x00, 0x00, 0x01, 0x13, 0x85, 0xe0, 0xff, 0x13, 0x35, 0x15, 0x00, 0x63, + 0x86, 0x00, 0x12, 0x63, 0x84, 0x0f, 0x12, 0x13, 0x84, 0x0b, 0x00, 0x13, 0x07, 0x00, 0x00, 0x93, 0x05, 0x01, 0x1b, + 0x13, 0x0f, 0x10, 0x00, 0x33, 0x86, 0xf5, 0x00, 0x93, 0x06, 0x01, 0x37, 0x13, 0x17, 0x57, 0x00, 0x33, 0x87, 0xe6, + 0x00, 0x83, 0x27, 0x07, 0x00, 0x03, 0x28, 0x47, 0x00, 0x83, 0x28, 0x87, 0x00, 0x83, 0x22, 0xc7, 0x00, 0x03, 0x23, + 0x07, 0x01, 0x83, 0x23, 0x47, 0x01, 0x03, 0x2e, 0x87, 0x01, 0x83, 0x2e, 0xc7, 0x01, 0x13, 0x07, 0x0f, 0x00, 0x13, + 0x8f, 0x05, 0x00, 0x83, 0x2f, 0x0f, 0xff, 0x83, 0x24, 0x4f, 0xff, 0x03, 0x29, 0x8f, 0xff, 0x83, 0x29, 0xcf, 0xff, + 0x03, 0x2a, 0x0f, 0x00, 0x03, 0x2b, 0x4f, 0x00, 0x83, 0x2b, 0x8f, 0x00, 0x83, 0x2c, 0xcf, 0x00, 0x33, 0xbd, 0xf7, + 0x01, 0xb3, 0x04, 0x98, 0x40, 0xb3, 0x8f, 0xf7, 0x41, 0xb3, 0x84, 0xa4, 0x41, 0x33, 0xbd, 0x28, 0x01, 0xb3, 0x89, + 0x32, 0x41, 0xb3, 0x89, 0xa9, 0x41, 0x33, 0x3d, 0x43, 0x01, 0x33, 0x8b, 0x63, 0x41, 0x33, 0x0b, 0xab, 0x41, 0x33, + 0x3d, 0x7e, 0x01, 0xb3, 0x8c, 0x9e, 0x41, 0xb3, 0x8a, 0x5f, 0x03, 0xb3, 0x8c, 0xac, 0x41, 0x33, 0xbd, 0x8f, 0x03, + 0xb3, 0x8f, 0x8f, 0x03, 0xb3, 0x84, 0x84, 0x03, 0xb3, 0x0a, 0x5d, 0x01, 0x33, 0x89, 0x28, 0x41, 0xb3, 0x89, 0x3f, + 0x03, 0xb3, 0x84, 0x9a, 0x00, 0xb3, 0xba, 0x2f, 0x03, 0xb3, 0x89, 0x3a, 0x01, 0x33, 0x0a, 0x43, 0x41, 0xb3, 0x8f, + 0x2f, 0x03, 0xb3, 0x8a, 0x6f, 0x03, 0xb3, 0x84, 0x24, 0x03, 0x33, 0xb9, 0x4f, 0x03, 0x33, 0x09, 0x59, 0x01, 0x33, + 0x0b, 0x7e, 0x41, 0xb3, 0x8f, 0x4f, 0x03, 0xb3, 0x8a, 0x9f, 0x03, 0xb3, 0x84, 0x99, 0x00, 0xb3, 0xb9, 0x6f, 0x03, + 0xb3, 0x89, 0x59, 0x01, 0xb3, 0x84, 0x44, 0x03, 0x13, 0x0f, 0x0f, 0x02, 0xb3, 0x04, 0x99, 0x00, 0xb3, 0x8a, 0x64, + 0x03, 0xb3, 0x8a, 0x59, 0x01, 0x33, 0x8c, 0x6f, 0x03, 0xe3, 0x1e, 0xcf, 0xf2, 0x33, 0x3f, 0x17, 0x00, 0x33, 0x0f, + 0xe7, 0x01, 0xe3, 0x60, 0x17, 0xf0, 0x93, 0x0b, 0x04, 0x00, 0x03, 0x24, 0x41, 0x0f, 0x63, 0x02, 0x05, 0x0e, 0x03, + 0x25, 0x01, 0x37, 0x83, 0x25, 0x41, 0x37, 0x03, 0x26, 0x81, 0x37, 0x83, 0x26, 0xc1, 0x37, 0x03, 0x27, 0x01, 0x39, + 0x83, 0x27, 0x41, 0x39, 0x03, 0x28, 0x81, 0x39, 0x83, 0x28, 0xc1, 0x39, 0x83, 0x22, 0x01, 0x38, 0x03, 0x23, 0x41, + 0x38, 0x83, 0x23, 0x81, 0x38, 0x03, 0x2e, 0xc1, 0x38, 0xb3, 0x3e, 0xe5, 0x00, 0xb3, 0x85, 0xf5, 0x40, 0x33, 0x05, + 0xe5, 0x40, 0x33, 0x37, 0x06, 0x01, 0xb3, 0x86, 0x16, 0x41, 0x83, 0x27, 0x01, 0x3a, 0x83, 0x28, 0x41, 0x3a, 0x33, + 0x06, 0x06, 0x41, 0x03, 0x28, 0x81, 0x3a, 0x03, 0x2f, 0xc1, 0x3a, 0xb3, 0x08, 0x13, 0x41, 0x33, 0xb3, 0xf2, 0x00, + 0xb3, 0x87, 0xf2, 0x40, 0xb3, 0x02, 0xee, 0x41, 0x33, 0xbe, 0x03, 0x01, 0x33, 0x88, 0x03, 0x41, 0xb3, 0x85, 0xd5, + 0x41, 0xb3, 0x86, 0xe6, 0x40, 0x33, 0x87, 0x68, 0x40, 0xb3, 0x08, 0x55, 0x03, 0xb3, 0x82, 0xc2, 0x41, 0x33, 0x33, + 0x85, 0x03, 0x33, 0x05, 0x85, 0x03, 0xb3, 0x85, 0x85, 0x03, 0xb3, 0x06, 0xd5, 0x02, 0xb3, 0x05, 0xb3, 0x00, 0x33, + 0x33, 0xc5, 0x02, 0xb3, 0x06, 0xd3, 0x00, 0x33, 0x05, 0xc5, 0x02, 0x33, 0x07, 0xe5, 0x02, 0xb3, 0x85, 0x15, 0x01, + 0xb3, 0x38, 0xf5, 0x02, 0x33, 0x87, 0xe8, 0x00, 0x33, 0x05, 0xf5, 0x02, 0xb3, 0x08, 0x55, 0x02, 0xb3, 0x85, 0xc5, + 0x02, 0x33, 0x36, 0x05, 0x03, 0x33, 0x06, 0x16, 0x01, 0xb3, 0x85, 0xb6, 0x00, 0xb3, 0x85, 0xf5, 0x02, 0xb3, 0x05, + 0xb7, 0x00, 0xb3, 0x8a, 0x05, 0x03, 0xb3, 0x0a, 0x56, 0x01, 0x33, 0x0c, 0x05, 0x03, 0x03, 0xa5, 0x0d, 0x00, 0x83, + 0xa5, 0x4d, 0x00, 0x33, 0x06, 0x55, 0x03, 0xb3, 0x36, 0x85, 0x03, 0xb3, 0x85, 0x85, 0x03, 0x33, 0x05, 0x85, 0x03, + 0xb3, 0x85, 0xb6, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x45, 0x15, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x04, 0x05, + 0x00, 0x6f, 0x10, 0x80, 0x38, 0x13, 0x8d, 0x0b, 0x00, 0x03, 0xa9, 0x8d, 0x00, 0x83, 0xa9, 0xcd, 0x00, 0x03, 0xaa, + 0x0d, 0x01, 0x83, 0xaa, 0x4d, 0x01, 0x03, 0xab, 0x8d, 0x01, 0x83, 0xab, 0xcd, 0x01, 0x03, 0xac, 0x0d, 0x02, 0x83, + 0xac, 0x4d, 0x02, 0x13, 0x85, 0x8d, 0x02, 0x23, 0x2e, 0xa1, 0x12, 0x23, 0x28, 0x21, 0x4d, 0x23, 0x2a, 0x31, 0x4d, + 0x23, 0x2c, 0x41, 0x4d, 0x23, 0x2e, 0x51, 0x4d, 0x23, 0x20, 0x61, 0x4f, 0x23, 0x22, 0x71, 0x4f, 0x23, 0x24, 0x81, + 0x4f, 0x23, 0x26, 0x91, 0x4f, 0x13, 0x05, 0x01, 0x18, 0x93, 0x05, 0x01, 0x4d, 0x13, 0x06, 0xc1, 0x13, 0x97, 0xc0, + 0xff, 0xff, 0xe7, 0x80, 0x00, 0x7f, 0x63, 0x1c, 0x04, 0x00, 0x13, 0x05, 0x01, 0x3f, 0x93, 0x05, 0x01, 0x4d, 0x13, + 0x06, 0xc1, 0x13, 0x97, 0xc0, 0xff, 0xff, 0xe7, 0x80, 0x80, 0x7d, 0x83, 0x24, 0xc1, 0x13, 0x83, 0xae, 0x04, 0x00, + 0x93, 0x84, 0x44, 0x00, 0x23, 0x2e, 0x91, 0x12, 0x03, 0x25, 0x41, 0x0e, 0x23, 0x20, 0xa0, 0x10, 0x03, 0x25, 0x01, + 0x0e, 0x23, 0x22, 0xa0, 0x10, 0x03, 0x25, 0x81, 0x13, 0x23, 0x24, 0xa0, 0x10, 0x03, 0x25, 0x41, 0x13, 0x23, 0x26, + 0xa0, 0x10, 0x03, 0x25, 0x01, 0x13, 0x23, 0x28, 0xa0, 0x10, 0x03, 0x25, 0xc1, 0x12, 0x23, 0x2a, 0xa0, 0x10, 0x03, + 0x25, 0x81, 0x12, 0x23, 0x2c, 0xa0, 0x10, 0x03, 0x25, 0x41, 0x12, 0x23, 0x2e, 0xa0, 0x10, 0x03, 0x25, 0x01, 0x08, + 0x23, 0x20, 0xa0, 0x12, 0x63, 0x00, 0x05, 0x08, 0x93, 0x05, 0x01, 0x2f, 0x03, 0x24, 0xc1, 0x11, 0x33, 0x84, 0x85, + 0x00, 0x13, 0x05, 0x40, 0x12, 0x03, 0x27, 0x01, 0x12, 0x83, 0x27, 0x01, 0x0d, 0x03, 0x28, 0xc1, 0x0c, 0x83, 0x28, + 0x81, 0x0c, 0x83, 0x22, 0x41, 0x0c, 0x03, 0x23, 0x01, 0x0c, 0x83, 0x23, 0xc1, 0x0b, 0x03, 0xa6, 0x05, 0xff, 0x83, + 0xa6, 0x45, 0xff, 0x23, 0x20, 0xc5, 0x00, 0x23, 0x22, 0xd5, 0x00, 0x03, 0xa6, 0x85, 0xff, 0x83, 0xa6, 0xc5, 0xff, + 0x23, 0x24, 0xc5, 0x00, 0x23, 0x26, 0xd5, 0x00, 0x03, 0xa6, 0x05, 0x00, 0x83, 0xa6, 0x45, 0x00, 0x23, 0x28, 0xc5, + 0x00, 0x23, 0x2a, 0xd5, 0x00, 0x03, 0xa6, 0x85, 0x00, 0x83, 0xa6, 0xc5, 0x00, 0x93, 0x85, 0x05, 0x02, 0x23, 0x2c, + 0xc5, 0x00, 0x23, 0x2e, 0xd5, 0x00, 0x13, 0x05, 0x05, 0x02, 0xe3, 0x9c, 0x85, 0xfa, 0x6f, 0x00, 0x40, 0x02, 0x13, + 0x05, 0x40, 0x12, 0x03, 0x27, 0x01, 0x12, 0x83, 0x27, 0x01, 0x0d, 0x03, 0x28, 0xc1, 0x0c, 0x83, 0x28, 0x81, 0x0c, + 0x83, 0x22, 0x41, 0x0c, 0x03, 0x23, 0x01, 0x0c, 0x83, 0x23, 0xc1, 0x0b, 0x23, 0x20, 0xa5, 0x01, 0x83, 0x25, 0x01, + 0x0f, 0x23, 0x22, 0xb5, 0x00, 0x83, 0x25, 0x41, 0x0d, 0x23, 0x24, 0xb5, 0x00, 0x83, 0x25, 0x81, 0x0d, 0x23, 0x26, + 0xb5, 0x00, 0x23, 0x28, 0x05, 0x01, 0x23, 0x2a, 0xf5, 0x00, 0x23, 0x2c, 0x55, 0x00, 0x23, 0x2e, 0x15, 0x01, 0x23, + 0x20, 0x75, 0x02, 0x23, 0x22, 0x65, 0x02, 0x23, 0x24, 0xe5, 0x02, 0x13, 0x05, 0xc5, 0x02, 0x63, 0x0e, 0x07, 0x04, + 0x93, 0x05, 0x01, 0x4a, 0x13, 0x16, 0x57, 0x00, 0x33, 0x86, 0xc5, 0x00, 0x83, 0xa6, 0x05, 0xff, 0x03, 0xa7, 0x45, + 0xff, 0x23, 0x20, 0xd5, 0x00, 0x23, 0x22, 0xe5, 0x00, 0x83, 0xa6, 0x85, 0xff, 0x03, 0xa7, 0xc5, 0xff, 0x23, 0x24, + 0xd5, 0x00, 0x23, 0x26, 0xe5, 0x00, 0x83, 0xa6, 0x05, 0x00, 0x03, 0xa7, 0x45, 0x00, 0x23, 0x28, 0xd5, 0x00, 0x23, + 0x2a, 0xe5, 0x00, 0x83, 0xa6, 0x85, 0x00, 0x03, 0xa7, 0xc5, 0x00, 0x93, 0x85, 0x05, 0x02, 0x23, 0x2c, 0xd5, 0x00, + 0x23, 0x2e, 0xe5, 0x00, 0x13, 0x05, 0x05, 0x02, 0xe3, 0x9c, 0xc5, 0xfa, 0x23, 0x20, 0x25, 0x01, 0x23, 0x22, 0x35, + 0x01, 0x23, 0x24, 0x45, 0x01, 0x23, 0x26, 0x55, 0x01, 0x23, 0x28, 0x65, 0x01, 0x23, 0x2a, 0x75, 0x01, 0x23, 0x2c, + 0x85, 0x01, 0x23, 0x2e, 0x95, 0x01, 0x23, 0x20, 0xd5, 0x03, 0xe3, 0x86, 0x0e, 0x72, 0x13, 0x0a, 0x45, 0x02, 0x37, + 0x55, 0x5f, 0x56, 0xb7, 0x55, 0x4d, 0x4d, 0x37, 0x56, 0x4b, 0x5f, 0xb7, 0x56, 0x00, 0x00, 0x37, 0x57, 0x44, 0x46, + 0xb7, 0x57, 0x45, 0x57, 0x37, 0x38, 0x00, 0x00, 0xb7, 0x48, 0x4d, 0x5f, 0xb7, 0x52, 0x54, 0x52, 0x37, 0x53, 0x5f, + 0x48, 0xb7, 0x53, 0x48, 0x5f, 0x37, 0x5e, 0x41, 0x43, 0x13, 0x0f, 0x95, 0x44, 0x93, 0x85, 0x35, 0xf4, 0x13, 0x06, + 0x66, 0x64, 0x13, 0x85, 0xf6, 0x65, 0x23, 0x2e, 0xa1, 0x10, 0x13, 0x05, 0xf7, 0xb5, 0x23, 0x22, 0xa1, 0x0a, 0x93, + 0x89, 0x67, 0x95, 0x13, 0x05, 0x68, 0x15, 0x23, 0x26, 0xa1, 0x08, 0x13, 0x85, 0x58, 0x14, 0x23, 0x2a, 0xa1, 0x08, + 0x13, 0x85, 0xf2, 0x35, 0x23, 0x28, 0xa1, 0x08, 0x13, 0x05, 0x33, 0x44, 0x23, 0x20, 0xa1, 0x0a, 0x13, 0x85, 0x13, + 0x34, 0x23, 0x2e, 0xa1, 0x08, 0x13, 0x05, 0xfe, 0xd5, 0x23, 0x2e, 0xa1, 0x06, 0x93, 0x06, 0x10, 0x03, 0x93, 0x0c, + 0x00, 0x02, 0x93, 0x0d, 0x01, 0x77, 0x13, 0x05, 0x10, 0x00, 0x23, 0x2c, 0xb1, 0x06, 0x23, 0x2a, 0xc1, 0x06, 0x93, + 0x8b, 0x0e, 0x00, 0x23, 0x2c, 0xa1, 0x08, 0x03, 0xac, 0x04, 0x00, 0x83, 0xaa, 0x44, 0x00, 0x03, 0xa5, 0x84, 0x00, + 0x23, 0x2c, 0xa1, 0x12, 0x03, 0xa5, 0xc4, 0x00, 0x23, 0x2a, 0xa1, 0x12, 0x03, 0xa5, 0x04, 0x01, 0x23, 0x28, 0xa1, + 0x12, 0x03, 0xa5, 0x44, 0x01, 0x23, 0x26, 0xa1, 0x12, 0x03, 0xa5, 0x84, 0x01, 0x23, 0x24, 0xa1, 0x12, 0x03, 0xa5, + 0xc4, 0x01, 0x23, 0x22, 0xa1, 0x12, 0x03, 0xa5, 0x04, 0x02, 0x03, 0xa7, 0x44, 0x02, 0x83, 0xa7, 0x84, 0x02, 0x03, + 0xa8, 0xc4, 0x02, 0x83, 0xa8, 0x04, 0x03, 0x83, 0xa2, 0x44, 0x03, 0x03, 0xa3, 0x84, 0x03, 0x83, 0xa3, 0xc4, 0x03, + 0x23, 0x28, 0xc1, 0x78, 0x23, 0x2a, 0xb1, 0x78, 0x13, 0x04, 0x0f, 0x00, 0x23, 0x2c, 0xe1, 0x79, 0x23, 0x0e, 0xd1, + 0x78, 0x93, 0x55, 0x87, 0x01, 0x13, 0x56, 0x07, 0x01, 0x93, 0x56, 0x87, 0x00, 0x13, 0x5e, 0x85, 0x01, 0x93, 0x5e, + 0x05, 0x01, 0x13, 0x5f, 0x85, 0x00, 0x93, 0x5f, 0x88, 0x01, 0x93, 0x50, 0x08, 0x01, 0x13, 0x59, 0x88, 0x00, 0x13, + 0xdd, 0x07, 0x01, 0x13, 0xdb, 0x87, 0x00, 0x23, 0x2a, 0xe1, 0x10, 0xa3, 0x00, 0xe1, 0x7a, 0x23, 0x28, 0xd1, 0x0e, + 0x23, 0x01, 0xd1, 0x7a, 0x23, 0x2a, 0xc1, 0x0e, 0xa3, 0x01, 0xc1, 0x7a, 0x23, 0x2c, 0xb1, 0x0e, 0x23, 0x02, 0xb1, + 0x7a, 0x23, 0x2c, 0xa1, 0x10, 0xa3, 0x0e, 0xa1, 0x78, 0x23, 0x22, 0xe1, 0x0f, 0x23, 0x0f, 0xe1, 0x79, 0x23, 0x24, + 0xd1, 0x0f, 0xa3, 0x0f, 0xd1, 0x79, 0x23, 0x26, 0xc1, 0x0f, 0x23, 0x00, 0xc1, 0x7b, 0x23, 0x26, 0x01, 0x11, 0xa3, + 0x04, 0x01, 0x7b, 0x23, 0x2c, 0x21, 0x0d, 0x23, 0x05, 0x21, 0x7b, 0x23, 0x2e, 0x11, 0x0c, 0xa3, 0x05, 0x11, 0x7a, + 0x23, 0x20, 0xf1, 0x0f, 0x23, 0x06, 0xf1, 0x7b, 0xa3, 0x02, 0xf1, 0x7a, 0x23, 0x28, 0x61, 0x0d, 0x23, 0x03, 0x61, + 0x7b, 0x23, 0x28, 0xf1, 0x10, 0x13, 0xd9, 0x87, 0x01, 0x23, 0x2a, 0xa1, 0x0d, 0xa3, 0x03, 0xa1, 0x7b, 0x23, 0x04, + 0x21, 0x7b, 0x13, 0xdb, 0x82, 0x00, 0xa3, 0x08, 0x51, 0x7a, 0x13, 0xdd, 0x02, 0x01, 0x23, 0x09, 0x61, 0x7b, 0x23, + 0x22, 0x51, 0x10, 0x13, 0xd5, 0x82, 0x01, 0xa3, 0x09, 0xa1, 0x7b, 0x23, 0x26, 0xa1, 0x0c, 0x23, 0x0a, 0xa1, 0x7a, + 0x13, 0xd5, 0x88, 0x00, 0xa3, 0x06, 0x11, 0x7b, 0x93, 0xd5, 0x08, 0x01, 0x23, 0x24, 0xa1, 0x0c, 0x23, 0x07, 0xa1, + 0x7a, 0x23, 0x24, 0x11, 0x11, 0x13, 0xd5, 0x88, 0x01, 0x23, 0x22, 0xb1, 0x0c, 0xa3, 0x07, 0xb1, 0x7a, 0x23, 0x20, + 0xa1, 0x0c, 0x23, 0x08, 0xa1, 0x7a, 0x13, 0xd5, 0x83, 0x00, 0xa3, 0x0c, 0x71, 0x7a, 0x93, 0xd5, 0x03, 0x01, 0x23, + 0x2e, 0xa1, 0x0a, 0x23, 0x0d, 0xa1, 0x7a, 0x23, 0x2e, 0x71, 0x0e, 0x13, 0xd5, 0x83, 0x01, 0x23, 0x2c, 0xb1, 0x0a, + 0xa3, 0x0d, 0xb1, 0x7a, 0x23, 0x2a, 0xa1, 0x0a, 0x23, 0x0e, 0xa1, 0x7a, 0x13, 0x55, 0x83, 0x00, 0xa3, 0x0a, 0x61, + 0x7a, 0x93, 0x55, 0x03, 0x01, 0x23, 0x28, 0xa1, 0x0a, 0x23, 0x0b, 0xa1, 0x7a, 0x23, 0x20, 0x61, 0x10, 0x13, 0x55, + 0x83, 0x01, 0x23, 0x26, 0xb1, 0x0a, 0xa3, 0x0b, 0xb1, 0x7a, 0x23, 0x24, 0xa1, 0x0a, 0x23, 0x0c, 0xa1, 0x7a, 0x13, + 0x05, 0x01, 0x4f, 0x93, 0x05, 0x01, 0x79, 0x13, 0x06, 0xd0, 0x02, 0x93, 0x06, 0xd0, 0x02, 0x97, 0xe0, 0xff, 0xff, + 0xe7, 0x80, 0x80, 0x8e, 0x03, 0x25, 0x41, 0x4f, 0x83, 0x25, 0x01, 0x4f, 0x23, 0x22, 0x51, 0x09, 0x33, 0x45, 0x55, + 0x01, 0x23, 0x24, 0x81, 0x09, 0xb3, 0xc5, 0x85, 0x01, 0x33, 0xe5, 0xa5, 0x00, 0xe3, 0x1e, 0x05, 0x48, 0x03, 0x25, + 0x81, 0x4f, 0x83, 0x25, 0xc1, 0x4f, 0x03, 0x26, 0x81, 0x13, 0x33, 0x45, 0xc5, 0x00, 0x03, 0x26, 0x41, 0x13, 0xb3, + 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0xe3, 0x1e, 0x05, 0x46, 0x03, 0x25, 0x01, 0x50, 0x83, 0x25, 0x41, 0x50, + 0x03, 0x26, 0x01, 0x13, 0x33, 0x45, 0xc5, 0x00, 0x03, 0x26, 0xc1, 0x12, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, + 0x00, 0xe3, 0x1e, 0x05, 0x44, 0x03, 0x25, 0x81, 0x50, 0x83, 0x25, 0xc1, 0x50, 0x03, 0x26, 0x81, 0x12, 0x33, 0x45, + 0xc5, 0x00, 0x03, 0x26, 0x41, 0x12, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0xe3, 0x1e, 0x05, 0x42, 0x93, + 0x84, 0x04, 0x04, 0x03, 0x25, 0x01, 0x12, 0xe3, 0x00, 0x05, 0x40, 0x23, 0x22, 0xa1, 0x05, 0x23, 0x24, 0x61, 0x05, + 0x23, 0x26, 0x21, 0x05, 0x13, 0x0d, 0x00, 0x00, 0x13, 0x05, 0x10, 0x00, 0x93, 0x06, 0x10, 0x03, 0x13, 0x09, 0x00, + 0x11, 0x03, 0x2b, 0x41, 0x09, 0x93, 0x8e, 0x0b, 0x00, 0x83, 0x2b, 0x01, 0x09, 0x13, 0x0f, 0x04, 0x00, 0x03, 0x24, + 0xc1, 0x08, 0x23, 0x2c, 0xd1, 0x01, 0x23, 0x2a, 0xe1, 0x01, 0x23, 0x22, 0x41, 0x07, 0x23, 0x2e, 0xa1, 0x00, 0x03, + 0xa5, 0x04, 0x00, 0x23, 0x20, 0xa1, 0x06, 0x03, 0xa5, 0x44, 0x00, 0x23, 0x2e, 0xa1, 0x04, 0x03, 0xa5, 0x84, 0x00, + 0x23, 0x2c, 0xa1, 0x04, 0x03, 0xa5, 0xc4, 0x00, 0x23, 0x28, 0xa1, 0x06, 0x03, 0xa5, 0x04, 0x01, 0x23, 0x26, 0xa1, + 0x06, 0x03, 0xa5, 0x44, 0x01, 0x23, 0x24, 0xa1, 0x06, 0x03, 0xa5, 0x84, 0x01, 0x23, 0x2a, 0xa1, 0x04, 0x03, 0xa5, + 0xc4, 0x01, 0x23, 0x28, 0xa1, 0x04, 0x03, 0xa5, 0x04, 0x02, 0x23, 0x2e, 0xa1, 0x02, 0x03, 0xa5, 0x44, 0x02, 0x23, + 0x2c, 0xa1, 0x02, 0x03, 0xa5, 0x84, 0x02, 0x23, 0x2a, 0xa1, 0x02, 0x03, 0xa5, 0xc4, 0x02, 0x23, 0x28, 0xa1, 0x02, + 0x03, 0xa5, 0x04, 0x03, 0x23, 0x26, 0xa1, 0x02, 0x03, 0xa5, 0x44, 0x03, 0x23, 0x24, 0xa1, 0x02, 0x03, 0xa5, 0x84, + 0x03, 0x23, 0x22, 0xa1, 0x02, 0x03, 0xa5, 0xc4, 0x03, 0x23, 0x20, 0xa1, 0x02, 0x13, 0x1a, 0x5d, 0x00, 0x23, 0x28, + 0x31, 0x79, 0x03, 0x25, 0x41, 0x0a, 0x23, 0x2a, 0xa1, 0x78, 0x03, 0x25, 0xc1, 0x11, 0x23, 0x1c, 0xa1, 0x78, 0x23, + 0x0d, 0xd1, 0x78, 0x03, 0x25, 0x41, 0x11, 0xa3, 0x0f, 0xa1, 0x78, 0x03, 0x25, 0x01, 0x0f, 0x23, 0x00, 0xa1, 0x7a, + 0x03, 0x25, 0x41, 0x0f, 0xa3, 0x00, 0xa1, 0x7a, 0x03, 0x25, 0x81, 0x0f, 0x23, 0x01, 0xa1, 0x7a, 0x03, 0x25, 0x81, + 0x11, 0xa3, 0x0d, 0xa1, 0x78, 0x03, 0x25, 0x41, 0x0e, 0x23, 0x0e, 0xa1, 0x78, 0x03, 0x25, 0x81, 0x0e, 0xa3, 0x0e, + 0xa1, 0x78, 0x03, 0x25, 0xc1, 0x0e, 0x23, 0x0f, 0xa1, 0x78, 0x03, 0x25, 0xc1, 0x10, 0xa3, 0x03, 0xa1, 0x7a, 0x03, + 0x25, 0x81, 0x0d, 0x23, 0x04, 0xa1, 0x7a, 0x03, 0x25, 0xc1, 0x0d, 0xa3, 0x04, 0xa1, 0x7a, 0x03, 0x25, 0x01, 0x0e, + 0x23, 0x05, 0xa1, 0x7a, 0x03, 0x25, 0x01, 0x11, 0xa3, 0x01, 0xa1, 0x7a, 0x03, 0x25, 0x01, 0x0d, 0x23, 0x02, 0xa1, + 0x7a, 0x03, 0x25, 0x41, 0x0d, 0xa3, 0x02, 0xa1, 0x7a, 0x03, 0x25, 0xc1, 0x04, 0x23, 0x03, 0xa1, 0x7a, 0x03, 0x25, + 0x41, 0x10, 0xa3, 0x07, 0xa1, 0x7a, 0x03, 0x25, 0x81, 0x04, 0x23, 0x08, 0xa1, 0x7a, 0x03, 0x25, 0x41, 0x04, 0xa3, + 0x08, 0xa1, 0x7a, 0x03, 0x25, 0xc1, 0x0c, 0x23, 0x09, 0xa1, 0x7a, 0x03, 0x25, 0x81, 0x10, 0xa3, 0x05, 0xa1, 0x7a, + 0x03, 0x25, 0x81, 0x0c, 0x23, 0x06, 0xa1, 0x7a, 0x03, 0x25, 0x41, 0x0c, 0xa3, 0x06, 0xa1, 0x7a, 0x03, 0x25, 0x01, + 0x0c, 0x23, 0x07, 0xa1, 0x7a, 0x03, 0x25, 0xc1, 0x0f, 0xa3, 0x0b, 0xa1, 0x7a, 0x03, 0x25, 0xc1, 0x0b, 0x23, 0x0c, + 0xa1, 0x7a, 0x03, 0x25, 0x81, 0x0b, 0xa3, 0x0c, 0xa1, 0x7a, 0x03, 0x25, 0x41, 0x0b, 0x23, 0x0d, 0xa1, 0x7a, 0x13, + 0x05, 0x01, 0x3b, 0xb3, 0x05, 0x45, 0x01, 0x03, 0x25, 0x01, 0x10, 0xa3, 0x09, 0xa1, 0x7a, 0x03, 0x25, 0x01, 0x0b, + 0x23, 0x0a, 0xa1, 0x7a, 0x03, 0x25, 0xc1, 0x0a, 0xa3, 0x0a, 0xa1, 0x7a, 0x03, 0x25, 0x81, 0x0a, 0x23, 0x0b, 0xa1, + 0x7a, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, 0xb1, 0x7b, 0x23, 0x20, 0xb1, 0x04, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, + 0xc0, 0x55, 0x13, 0x05, 0x01, 0x51, 0x93, 0x05, 0x01, 0x79, 0x13, 0x06, 0xb0, 0x04, 0x93, 0x06, 0xb0, 0x04, 0x97, + 0xd0, 0xff, 0xff, 0xe7, 0x80, 0xc0, 0x64, 0x13, 0x1d, 0x3d, 0x00, 0x23, 0x28, 0x01, 0x78, 0x23, 0x2a, 0x01, 0x78, + 0x23, 0x2c, 0x01, 0x78, 0x23, 0x2e, 0x01, 0x78, 0x23, 0x20, 0x01, 0x7a, 0x23, 0x22, 0x01, 0x7a, 0x23, 0x24, 0x01, + 0x7a, 0x23, 0x26, 0x01, 0x7a, 0x23, 0x20, 0x01, 0x66, 0x23, 0x22, 0x01, 0x66, 0x23, 0x24, 0x01, 0x66, 0x23, 0x26, + 0x01, 0x66, 0x23, 0x28, 0x01, 0x66, 0x23, 0x2a, 0x01, 0x66, 0x23, 0x2c, 0x01, 0x66, 0x23, 0x2e, 0x01, 0x66, 0x23, + 0x28, 0x01, 0x76, 0x23, 0x2a, 0x01, 0x76, 0x23, 0x2c, 0x01, 0x76, 0x23, 0x2e, 0x01, 0x76, 0x23, 0x20, 0x01, 0x78, + 0x23, 0x22, 0x01, 0x78, 0x23, 0x24, 0x01, 0x78, 0x23, 0x26, 0x01, 0x78, 0x23, 0x28, 0x01, 0x64, 0x23, 0x2a, 0x01, + 0x64, 0x23, 0x2c, 0x01, 0x64, 0x23, 0x2e, 0x01, 0x64, 0x13, 0x05, 0x01, 0x36, 0x33, 0x0d, 0xa5, 0x01, 0x83, 0x20, + 0x0d, 0x00, 0x03, 0x28, 0x4d, 0x00, 0x23, 0x20, 0x01, 0x64, 0x23, 0x22, 0x01, 0x64, 0x23, 0x24, 0x01, 0x64, 0x23, + 0x26, 0x01, 0x64, 0x03, 0x26, 0x01, 0x08, 0x13, 0x0c, 0x01, 0x56, 0x63, 0x06, 0x06, 0x4c, 0x83, 0x25, 0x01, 0x23, + 0x83, 0x26, 0x41, 0x23, 0x03, 0x27, 0x81, 0x23, 0x83, 0x27, 0xc1, 0x23, 0x23, 0x20, 0xb1, 0x7a, 0x23, 0x22, 0xd1, + 0x7a, 0x23, 0x24, 0xe1, 0x7a, 0x23, 0x26, 0xf1, 0x7a, 0x83, 0x25, 0x01, 0x22, 0x83, 0x26, 0x41, 0x22, 0x03, 0x27, + 0x81, 0x22, 0x83, 0x27, 0xc1, 0x22, 0x23, 0x28, 0xb1, 0x78, 0x23, 0x2a, 0xd1, 0x78, 0x23, 0x2c, 0xe1, 0x78, 0x23, + 0x2e, 0xf1, 0x78, 0x13, 0x05, 0x10, 0x00, 0x63, 0x02, 0xa6, 0x48, 0x93, 0x0a, 0x01, 0x24, 0x83, 0xc5, 0xca, 0x01, + 0x83, 0xc6, 0xda, 0x01, 0x03, 0xc7, 0xea, 0x01, 0x83, 0xc7, 0xfa, 0x01, 0x23, 0x26, 0x01, 0x01, 0x03, 0xc8, 0x8a, + 0x01, 0x83, 0xc8, 0x9a, 0x01, 0x83, 0xc2, 0xaa, 0x01, 0x03, 0xc3, 0xba, 0x01, 0x83, 0xc3, 0x4a, 0x01, 0x03, 0xce, + 0x5a, 0x01, 0x83, 0xce, 0x6a, 0x01, 0x03, 0xcf, 0x7a, 0x01, 0x83, 0xcf, 0x0a, 0x01, 0x23, 0x28, 0x41, 0x01, 0x03, + 0xca, 0x1a, 0x01, 0x03, 0xcc, 0x2a, 0x01, 0x03, 0xcd, 0x3a, 0x01, 0x93, 0x96, 0x86, 0x00, 0x13, 0x17, 0x07, 0x01, + 0x93, 0x97, 0x87, 0x01, 0xb3, 0xe5, 0xb6, 0x00, 0x23, 0x24, 0xb1, 0x00, 0xb3, 0xe6, 0xe7, 0x00, 0x93, 0x85, 0x00, + 0x00, 0x83, 0xc0, 0xca, 0x00, 0x03, 0xc5, 0xda, 0x00, 0x03, 0xc6, 0xea, 0x00, 0x03, 0xc7, 0xfa, 0x00, 0x93, 0x98, + 0x88, 0x00, 0x93, 0x97, 0x02, 0x01, 0x13, 0x13, 0x83, 0x01, 0x13, 0x1e, 0x8e, 0x00, 0x93, 0x9e, 0x0e, 0x01, 0x13, + 0x1f, 0x8f, 0x01, 0xb3, 0xe2, 0x08, 0x01, 0xb3, 0x67, 0xf3, 0x00, 0x33, 0x68, 0x7e, 0x00, 0xb3, 0x68, 0xdf, 0x01, + 0x03, 0xc3, 0x8a, 0x00, 0x83, 0xc3, 0x9a, 0x00, 0x03, 0xce, 0xaa, 0x00, 0x83, 0xce, 0xba, 0x00, 0x13, 0x1a, 0x8a, + 0x00, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, 0x13, 0x15, 0x85, 0x00, 0x13, 0x16, 0x06, 0x01, 0x13, 0x17, + 0x87, 0x01, 0x33, 0x6f, 0xfa, 0x01, 0xb3, 0x6f, 0x8d, 0x01, 0x33, 0x65, 0x15, 0x00, 0x93, 0x80, 0x05, 0x00, 0x33, + 0x66, 0xc7, 0x00, 0x03, 0xc7, 0x4a, 0x00, 0x03, 0xca, 0x5a, 0x00, 0x03, 0xcc, 0x6a, 0x00, 0x03, 0xcd, 0x7a, 0x00, + 0x93, 0x93, 0x83, 0x00, 0x13, 0x1e, 0x0e, 0x01, 0x93, 0x9e, 0x8e, 0x01, 0x13, 0x1a, 0x8a, 0x00, 0x13, 0x1c, 0x0c, + 0x01, 0x13, 0x1d, 0x8d, 0x01, 0x33, 0xe3, 0x63, 0x00, 0xb3, 0xe3, 0xce, 0x01, 0x33, 0x67, 0xea, 0x00, 0x03, 0xce, + 0x0a, 0x00, 0x83, 0xce, 0x1a, 0x00, 0x33, 0x6a, 0x8d, 0x01, 0x03, 0xcc, 0x2a, 0x00, 0x03, 0xcd, 0x3a, 0x00, 0x93, + 0x0a, 0x01, 0x26, 0x93, 0x9e, 0x8e, 0x00, 0x33, 0xee, 0xce, 0x01, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, + 0xb3, 0x6e, 0x8d, 0x01, 0x13, 0x0c, 0x01, 0x56, 0x83, 0x25, 0x81, 0x00, 0xb3, 0xe5, 0xb6, 0x00, 0xb3, 0xe6, 0x57, + 0x00, 0xb3, 0xe7, 0x08, 0x01, 0x33, 0xe8, 0xef, 0x01, 0x33, 0x65, 0xa6, 0x00, 0x33, 0xe6, 0x63, 0x00, 0x33, 0x67, + 0xea, 0x00, 0x03, 0x2a, 0x01, 0x01, 0xb3, 0xe8, 0xce, 0x01, 0x23, 0x28, 0x01, 0x67, 0x03, 0x28, 0xc1, 0x00, 0x23, + 0x2a, 0xf1, 0x66, 0x23, 0x2c, 0xd1, 0x66, 0x23, 0x2e, 0xb1, 0x66, 0x23, 0x20, 0x11, 0x67, 0x23, 0x22, 0xe1, 0x66, + 0x23, 0x24, 0xc1, 0x66, 0x03, 0x26, 0x01, 0x08, 0x23, 0x26, 0xa1, 0x66, 0x13, 0x05, 0x30, 0x00, 0x63, 0x66, 0xa6, + 0x2e, 0x03, 0xc5, 0xca, 0x01, 0x83, 0xc5, 0xda, 0x01, 0x03, 0xc6, 0xea, 0x01, 0x83, 0xc6, 0xfa, 0x01, 0x03, 0xc7, + 0x8a, 0x01, 0x83, 0xc7, 0x9a, 0x01, 0x03, 0xc8, 0xaa, 0x01, 0x83, 0xc8, 0xba, 0x01, 0x83, 0xc2, 0x4a, 0x01, 0x03, + 0xc3, 0x5a, 0x01, 0x83, 0xc3, 0x6a, 0x01, 0x03, 0xce, 0x7a, 0x01, 0x83, 0xce, 0x0a, 0x01, 0x03, 0xcf, 0x1a, 0x01, + 0x83, 0xcf, 0x2a, 0x01, 0x03, 0xca, 0x3a, 0x01, 0x93, 0x95, 0x85, 0x00, 0x13, 0x16, 0x06, 0x01, 0x93, 0x96, 0x86, + 0x01, 0xb3, 0xe5, 0xa5, 0x00, 0xb3, 0xe6, 0xc6, 0x00, 0x03, 0xc5, 0xca, 0x00, 0x03, 0xc6, 0xda, 0x00, 0x03, 0xcc, + 0xea, 0x00, 0x03, 0xcd, 0xfa, 0x00, 0x93, 0x97, 0x87, 0x00, 0x13, 0x18, 0x08, 0x01, 0x93, 0x98, 0x88, 0x01, 0x13, + 0x13, 0x83, 0x00, 0x93, 0x93, 0x03, 0x01, 0x13, 0x1e, 0x8e, 0x01, 0x33, 0xe7, 0xe7, 0x00, 0xb3, 0xe7, 0x08, 0x01, + 0x33, 0x68, 0x53, 0x00, 0xb3, 0x68, 0x7e, 0x00, 0x83, 0xc2, 0x8a, 0x00, 0x03, 0xc3, 0x9a, 0x00, 0x83, 0xc3, 0xaa, + 0x00, 0x03, 0xce, 0xba, 0x00, 0x13, 0x1f, 0x8f, 0x00, 0x93, 0x9f, 0x0f, 0x01, 0x13, 0x1a, 0x8a, 0x01, 0x13, 0x16, + 0x86, 0x00, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, 0xb3, 0x6e, 0xdf, 0x01, 0x33, 0x6f, 0xfa, 0x01, 0x33, + 0x65, 0xa6, 0x00, 0x33, 0x66, 0x8d, 0x01, 0x83, 0xcf, 0x4a, 0x00, 0x03, 0xca, 0x5a, 0x00, 0x03, 0xcc, 0x6a, 0x00, + 0x03, 0xcd, 0x7a, 0x00, 0x13, 0x13, 0x83, 0x00, 0x93, 0x93, 0x03, 0x01, 0x13, 0x1e, 0x8e, 0x01, 0x13, 0x1a, 0x8a, + 0x00, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, 0xb3, 0x62, 0x53, 0x00, 0x33, 0x63, 0x7e, 0x00, 0xb3, 0x63, + 0xfa, 0x01, 0x03, 0xce, 0x0a, 0x00, 0x83, 0xcf, 0x1a, 0x00, 0x33, 0x6a, 0x8d, 0x01, 0x03, 0xcc, 0x2a, 0x00, 0x03, + 0xcd, 0x3a, 0x00, 0x93, 0x9f, 0x8f, 0x00, 0x33, 0xee, 0xcf, 0x01, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, + 0xb3, 0x6f, 0x8d, 0x01, 0x13, 0x0c, 0x01, 0x56, 0xb3, 0xe5, 0xb6, 0x00, 0x33, 0xe7, 0xe7, 0x00, 0xb3, 0xe6, 0x08, + 0x01, 0xb3, 0x67, 0xdf, 0x01, 0x33, 0x65, 0xa6, 0x00, 0x33, 0x66, 0x53, 0x00, 0x33, 0x68, 0x7a, 0x00, 0x03, 0x2a, + 0x01, 0x01, 0xb3, 0xe8, 0xcf, 0x01, 0x23, 0x20, 0xf1, 0x78, 0x23, 0x22, 0xd1, 0x78, 0x23, 0x24, 0xe1, 0x78, 0x23, + 0x26, 0xb1, 0x78, 0x23, 0x28, 0x11, 0x77, 0x23, 0x2a, 0x01, 0x77, 0x03, 0x28, 0xc1, 0x00, 0x23, 0x2c, 0xc1, 0x76, + 0x23, 0x2e, 0xa1, 0x76, 0x13, 0x05, 0x30, 0x00, 0x83, 0x25, 0x01, 0x08, 0x63, 0x8a, 0xa5, 0x16, 0x93, 0x0a, 0x01, + 0x28, 0x03, 0xc5, 0xca, 0x01, 0x83, 0xc5, 0xda, 0x01, 0x03, 0xc6, 0xea, 0x01, 0x83, 0xc6, 0xfa, 0x01, 0x03, 0xc7, + 0x8a, 0x01, 0x83, 0xc7, 0x9a, 0x01, 0x03, 0xc8, 0xaa, 0x01, 0x83, 0xc8, 0xba, 0x01, 0x83, 0xc2, 0x4a, 0x01, 0x03, + 0xc3, 0x5a, 0x01, 0x83, 0xc3, 0x6a, 0x01, 0x03, 0xce, 0x7a, 0x01, 0x83, 0xce, 0x0a, 0x01, 0x03, 0xcf, 0x1a, 0x01, + 0x83, 0xcf, 0x2a, 0x01, 0x03, 0xca, 0x3a, 0x01, 0x93, 0x95, 0x85, 0x00, 0x13, 0x16, 0x06, 0x01, 0x93, 0x96, 0x86, + 0x01, 0xb3, 0xe5, 0xa5, 0x00, 0xb3, 0xe6, 0xc6, 0x00, 0x03, 0xc5, 0xca, 0x00, 0x03, 0xc6, 0xda, 0x00, 0x03, 0xcc, + 0xea, 0x00, 0x03, 0xcd, 0xfa, 0x00, 0x93, 0x97, 0x87, 0x00, 0x13, 0x18, 0x08, 0x01, 0x93, 0x98, 0x88, 0x01, 0x13, + 0x13, 0x83, 0x00, 0x93, 0x93, 0x03, 0x01, 0x13, 0x1e, 0x8e, 0x01, 0x33, 0xe7, 0xe7, 0x00, 0xb3, 0xe7, 0x08, 0x01, + 0x33, 0x68, 0x53, 0x00, 0xb3, 0x68, 0x7e, 0x00, 0x83, 0xc2, 0x8a, 0x00, 0x03, 0xc3, 0x9a, 0x00, 0x83, 0xc3, 0xaa, + 0x00, 0x03, 0xce, 0xba, 0x00, 0x13, 0x1f, 0x8f, 0x00, 0x93, 0x9f, 0x0f, 0x01, 0x13, 0x1a, 0x8a, 0x01, 0x13, 0x16, + 0x86, 0x00, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, 0xb3, 0x6e, 0xdf, 0x01, 0x33, 0x6f, 0xfa, 0x01, 0x33, + 0x65, 0xa6, 0x00, 0x33, 0x66, 0x8d, 0x01, 0x83, 0xcf, 0x4a, 0x00, 0x03, 0xca, 0x5a, 0x00, 0x03, 0xcc, 0x6a, 0x00, + 0x03, 0xcd, 0x7a, 0x00, 0x13, 0x13, 0x83, 0x00, 0x93, 0x93, 0x03, 0x01, 0x13, 0x1e, 0x8e, 0x01, 0x13, 0x1a, 0x8a, + 0x00, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, 0xb3, 0x62, 0x53, 0x00, 0x33, 0x63, 0x7e, 0x00, 0xb3, 0x63, + 0xfa, 0x01, 0x03, 0xce, 0x0a, 0x00, 0x83, 0xcf, 0x1a, 0x00, 0x33, 0x6a, 0x8d, 0x01, 0x03, 0xcc, 0x2a, 0x00, 0x03, + 0xcd, 0x3a, 0x00, 0x93, 0x9f, 0x8f, 0x00, 0x33, 0xee, 0xcf, 0x01, 0x13, 0x1c, 0x0c, 0x01, 0x13, 0x1d, 0x8d, 0x01, + 0xb3, 0x6f, 0x8d, 0x01, 0x13, 0x0c, 0x01, 0x56, 0xb3, 0xe5, 0xb6, 0x00, 0x33, 0xe7, 0xe7, 0x00, 0xb3, 0xe6, 0x08, + 0x01, 0xb3, 0x67, 0xdf, 0x01, 0x33, 0x65, 0xa6, 0x00, 0x33, 0x66, 0x53, 0x00, 0x33, 0x68, 0x7a, 0x00, 0x03, 0x2a, + 0x01, 0x01, 0xb3, 0xe8, 0xcf, 0x01, 0x23, 0x28, 0xf1, 0x64, 0x23, 0x2a, 0xd1, 0x64, 0x23, 0x2c, 0xe1, 0x64, 0x23, + 0x2e, 0xb1, 0x64, 0x23, 0x20, 0x11, 0x65, 0x23, 0x22, 0x01, 0x65, 0x03, 0x28, 0xc1, 0x00, 0x23, 0x24, 0xc1, 0x64, + 0x23, 0x26, 0xa1, 0x64, 0x93, 0x84, 0x04, 0x04, 0x93, 0x05, 0x01, 0x37, 0xb3, 0x85, 0x45, 0x01, 0x13, 0x0d, 0x01, + 0x3f, 0x33, 0x0d, 0x4d, 0x01, 0x03, 0x25, 0x01, 0x15, 0x03, 0x26, 0x41, 0x15, 0x83, 0x26, 0x81, 0x15, 0x03, 0x27, + 0xc1, 0x15, 0x23, 0x20, 0xa1, 0x54, 0x23, 0x22, 0xc1, 0x54, 0x23, 0x24, 0xd1, 0x54, 0x23, 0x26, 0xe1, 0x54, 0x03, + 0x25, 0x01, 0x14, 0x03, 0x26, 0x41, 0x14, 0x83, 0x26, 0x81, 0x14, 0x03, 0x27, 0xc1, 0x14, 0x23, 0x28, 0xa1, 0x52, + 0x23, 0x2a, 0xc1, 0x52, 0x23, 0x2c, 0xd1, 0x52, 0x23, 0x2e, 0xe1, 0x52, 0x23, 0x28, 0x11, 0x54, 0x23, 0x2a, 0x01, + 0x55, 0x23, 0x2c, 0x01, 0x54, 0x23, 0x2e, 0x01, 0x54, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, 0x0c, 0x00, 0x97, 0x50, + 0x00, 0x00, 0xe7, 0x80, 0x80, 0xf6, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, 0x01, 0x58, 0x93, 0x05, 0x0d, 0x00, 0x97, + 0x50, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xf5, 0x93, 0x05, 0x01, 0x18, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, 0x01, 0x5a, + 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xf4, 0x93, 0x05, 0x01, 0x79, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, 0x01, + 0x5c, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xf2, 0x93, 0x05, 0x01, 0x66, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, + 0x01, 0x5e, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xf1, 0x93, 0x05, 0x01, 0x77, 0x13, 0x06, 0x00, 0x02, 0x13, + 0x05, 0x01, 0x60, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xf0, 0x93, 0x05, 0x01, 0x64, 0x13, 0x06, 0x00, 0x02, + 0x13, 0x05, 0x01, 0x62, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xef, 0x13, 0x05, 0x01, 0x66, 0x13, 0x06, 0x00, + 0x11, 0x93, 0x05, 0x00, 0x00, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xf0, 0x13, 0x0a, 0x00, 0x00, 0x13, 0x0c, + 0x00, 0x00, 0x93, 0x0a, 0xe1, 0x79, 0x6f, 0x00, 0x40, 0x01, 0x13, 0x0a, 0x1a, 0x00, 0x33, 0x0c, 0x8d, 0x01, 0x13, + 0x05, 0x90, 0x00, 0x63, 0x0a, 0xaa, 0x0a, 0x23, 0x28, 0x31, 0x79, 0x23, 0x2a, 0x71, 0x79, 0x23, 0x2c, 0x61, 0x79, + 0x23, 0x1e, 0x81, 0x78, 0x93, 0x05, 0x01, 0x51, 0x13, 0x06, 0x00, 0x02, 0x13, 0x0d, 0x00, 0x02, 0x13, 0x85, 0x0a, + 0x00, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xe9, 0x13, 0x55, 0x0a, 0x01, 0x23, 0x1f, 0x41, 0x7b, 0x23, 0x10, + 0xa1, 0x7c, 0x13, 0x05, 0x01, 0x77, 0x93, 0x05, 0x01, 0x79, 0x13, 0x06, 0x20, 0x03, 0x93, 0x06, 0x20, 0x03, 0x97, + 0xd0, 0xff, 0xff, 0xe7, 0x80, 0x80, 0xf7, 0x13, 0x05, 0x10, 0x0f, 0x63, 0x64, 0xac, 0x00, 0x33, 0x0d, 0x89, 0x41, + 0xe3, 0x0c, 0x0d, 0xf8, 0x13, 0x05, 0x00, 0x00, 0x33, 0x06, 0x89, 0x41, 0x93, 0x05, 0x01, 0x66, 0xb3, 0x85, 0x85, + 0x01, 0x93, 0x36, 0x16, 0x11, 0xb3, 0x06, 0xd0, 0x40, 0x33, 0xf6, 0xc6, 0x00, 0x93, 0x06, 0x01, 0x53, 0xb3, 0x86, + 0x86, 0x01, 0x63, 0x02, 0xa6, 0x58, 0x63, 0x04, 0x95, 0x57, 0x33, 0x87, 0xa6, 0x00, 0xb3, 0x87, 0xad, 0x00, 0x03, + 0x47, 0x07, 0x00, 0x83, 0xc7, 0x07, 0x00, 0x33, 0x88, 0xa5, 0x00, 0x33, 0xc7, 0xe7, 0x00, 0x13, 0x05, 0x15, 0x00, + 0x23, 0x00, 0xe8, 0x00, 0xe3, 0x1c, 0xad, 0xfc, 0x6f, 0xf0, 0x5f, 0xf4, 0x93, 0x05, 0x01, 0x66, 0x13, 0x06, 0x00, + 0x11, 0x13, 0x05, 0xa1, 0x79, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xdf, 0x03, 0x25, 0x01, 0x0a, 0x23, 0x28, + 0xa1, 0x78, 0x03, 0x25, 0xc1, 0x09, 0x23, 0x2a, 0xa1, 0x78, 0x23, 0x1c, 0x81, 0x78, 0x13, 0x05, 0x01, 0x77, 0x93, + 0x05, 0x01, 0x79, 0x13, 0x06, 0xa0, 0x11, 0x93, 0x06, 0xa0, 0x11, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0xed, + 0x03, 0x25, 0x41, 0x77, 0x83, 0x25, 0x01, 0x77, 0x03, 0x2c, 0xc1, 0x05, 0x33, 0x45, 0x85, 0x01, 0x83, 0x2a, 0x01, + 0x06, 0xb3, 0xc5, 0x55, 0x01, 0x33, 0xe5, 0xa5, 0x00, 0x03, 0x2a, 0x41, 0x06, 0x03, 0x2d, 0x81, 0x05, 0x63, 0x1c, + 0x05, 0x2a, 0x03, 0x25, 0x81, 0x77, 0x83, 0x25, 0xc1, 0x77, 0x33, 0x45, 0xa5, 0x01, 0x03, 0x26, 0x01, 0x07, 0xb3, + 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x1e, 0x05, 0x28, 0x03, 0x25, 0x01, 0x78, 0x83, 0x25, 0x41, 0x78, + 0x03, 0x26, 0xc1, 0x06, 0x33, 0x45, 0xc5, 0x00, 0x03, 0x26, 0x81, 0x06, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, + 0x00, 0x63, 0x1e, 0x05, 0x26, 0x03, 0x25, 0x81, 0x78, 0x83, 0x25, 0xc1, 0x78, 0x03, 0x26, 0x41, 0x05, 0x33, 0x45, + 0xc5, 0x00, 0x03, 0x26, 0x01, 0x05, 0xb3, 0xc5, 0xc5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x1e, 0x05, 0x24, 0x23, + 0x28, 0x31, 0x79, 0x03, 0x25, 0xc1, 0x07, 0x23, 0x2a, 0xa1, 0x78, 0x03, 0x25, 0xc1, 0x11, 0x23, 0x1c, 0xa1, 0x78, + 0x13, 0x05, 0x10, 0x03, 0x23, 0x0d, 0xa1, 0x78, 0x93, 0x05, 0x01, 0x51, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, 0xb1, + 0x79, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xd1, 0x13, 0x06, 0x00, 0x02, 0x13, 0x05, 0xb1, 0x7b, 0x83, 0x25, + 0x01, 0x04, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xd0, 0x93, 0x05, 0x01, 0x77, 0x13, 0x06, 0x00, 0x02, 0x13, + 0x05, 0xb1, 0x7d, 0x97, 0x50, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xcf, 0x13, 0x05, 0x01, 0x66, 0x93, 0x05, 0x01, 0x79, + 0x13, 0x06, 0xb0, 0x06, 0x93, 0x06, 0xb0, 0x06, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0x00, 0xde, 0x03, 0x25, 0x41, + 0x66, 0x83, 0x25, 0x01, 0x66, 0x83, 0x26, 0x81, 0x03, 0x33, 0x45, 0xd5, 0x00, 0x03, 0x26, 0xc1, 0x03, 0xb3, 0xc5, + 0xc5, 0x00, 0x33, 0xe5, 0xa5, 0x00, 0x03, 0x27, 0x41, 0x03, 0x83, 0x27, 0x01, 0x03, 0x03, 0x28, 0xc1, 0x02, 0x83, + 0x28, 0x81, 0x02, 0x83, 0x22, 0x41, 0x02, 0x03, 0x23, 0x01, 0x02, 0x63, 0x18, 0x05, 0x1c, 0x03, 0x25, 0x81, 0x66, + 0x83, 0x25, 0xc1, 0x66, 0x33, 0x45, 0xe5, 0x00, 0xb3, 0xc5, 0xf5, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x1c, 0x05, + 0x1a, 0x03, 0x25, 0x01, 0x67, 0x83, 0x25, 0x41, 0x67, 0x33, 0x45, 0x05, 0x01, 0xb3, 0xc5, 0x15, 0x01, 0x33, 0x65, + 0xb5, 0x00, 0x63, 0x10, 0x05, 0x1a, 0x03, 0x25, 0x81, 0x67, 0x83, 0x25, 0xc1, 0x67, 0x33, 0x45, 0x55, 0x00, 0xb3, + 0xc5, 0x65, 0x00, 0x33, 0x65, 0xb5, 0x00, 0x63, 0x14, 0x05, 0x18, 0x83, 0x23, 0x01, 0x04, 0x03, 0xa5, 0x03, 0x00, + 0x83, 0xa5, 0x43, 0x00, 0x23, 0x20, 0xaa, 0x00, 0x23, 0x22, 0xba, 0x00, 0x03, 0xa5, 0x83, 0x00, 0x83, 0xa5, 0xc3, + 0x00, 0x23, 0x24, 0xaa, 0x00, 0x23, 0x26, 0xba, 0x00, 0x03, 0xa5, 0x03, 0x01, 0x83, 0xa5, 0x43, 0x01, 0x23, 0x28, + 0xaa, 0x00, 0x23, 0x2a, 0xba, 0x00, 0x03, 0xa5, 0x83, 0x01, 0x83, 0xa5, 0xc3, 0x01, 0x23, 0x2c, 0xaa, 0x00, 0x23, + 0x2e, 0xba, 0x00, 0x03, 0x25, 0x81, 0x08, 0x23, 0x20, 0xaa, 0x02, 0x03, 0x25, 0x41, 0x08, 0x23, 0x22, 0xaa, 0x02, + 0x03, 0x25, 0x81, 0x13, 0x23, 0x24, 0xaa, 0x02, 0x03, 0x25, 0x41, 0x13, 0x23, 0x26, 0xaa, 0x02, 0x03, 0x25, 0x01, + 0x13, 0x23, 0x28, 0xaa, 0x02, 0x03, 0x25, 0xc1, 0x12, 0x23, 0x2a, 0xaa, 0x02, 0x03, 0x25, 0x81, 0x12, 0x23, 0x2c, + 0xaa, 0x02, 0x03, 0x25, 0x41, 0x12, 0x23, 0x2e, 0xaa, 0x02, 0x23, 0x20, 0x5a, 0x05, 0x23, 0x22, 0x8a, 0x05, 0x23, + 0x24, 0xaa, 0x05, 0x03, 0x25, 0x01, 0x07, 0x23, 0x26, 0xaa, 0x04, 0x03, 0x25, 0xc1, 0x06, 0x23, 0x28, 0xaa, 0x04, + 0x03, 0x25, 0x81, 0x06, 0x23, 0x2a, 0xaa, 0x04, 0x03, 0x25, 0x41, 0x05, 0x23, 0x2c, 0xaa, 0x04, 0x03, 0x25, 0x01, + 0x05, 0x23, 0x2e, 0xaa, 0x04, 0x23, 0x20, 0xca, 0x06, 0x23, 0x22, 0xda, 0x06, 0x23, 0x24, 0xea, 0x06, 0x23, 0x26, + 0xfa, 0x06, 0x23, 0x28, 0x0a, 0x07, 0x23, 0x2a, 0x1a, 0x07, 0x23, 0x2c, 0x5a, 0x06, 0x23, 0x2e, 0x6a, 0x06, 0x83, + 0x25, 0x01, 0x12, 0x03, 0x2d, 0xc1, 0x01, 0x33, 0x35, 0xbd, 0x00, 0x13, 0x0a, 0x0a, 0x08, 0x33, 0x05, 0xad, 0x00, + 0x83, 0x2e, 0x81, 0x01, 0x03, 0x2f, 0x41, 0x01, 0x93, 0x06, 0x10, 0x03, 0x63, 0x62, 0xbd, 0xc4, 0x6f, 0x00, 0x00, + 0x01, 0x93, 0x06, 0x10, 0x03, 0x93, 0x8e, 0x0b, 0x00, 0x13, 0x0f, 0x04, 0x00, 0x03, 0x27, 0x81, 0x09, 0x33, 0x35, + 0xd7, 0x01, 0x33, 0x05, 0xa7, 0x00, 0x83, 0x25, 0x81, 0x07, 0x03, 0x26, 0x41, 0x07, 0x63, 0x6c, 0xd7, 0x97, 0x13, + 0x05, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x23, + 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x27, 0x93, 0x05, 0x20, 0x04, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x80, + 0xfb, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x3d, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, 0x3f, 0x93, 0x05, + 0xb0, 0x02, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xf9, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x45, 0x28, 0x37, + 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x2b, 0x93, 0x05, 0x00, 0x03, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xf8, + 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x45, 0x2c, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, 0x2f, 0x93, 0x05, 0xb0, + 0x02, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xf6, 0x13, 0x07, 0x00, 0x00, 0x13, 0x06, 0x81, 0x31, 0x93, 0x06, + 0x01, 0x2e, 0x83, 0x24, 0x81, 0x10, 0x03, 0x29, 0x41, 0x10, 0x6f, 0x00, 0xc0, 0x01, 0x93, 0x87, 0x15, 0x00, 0x13, + 0x06, 0x06, 0x02, 0x13, 0x87, 0x05, 0x00, 0x93, 0x85, 0x07, 0x00, 0x63, 0x94, 0xe7, 0x01, 0x6f, 0xe0, 0xcf, 0xa2, + 0x13, 0x17, 0x57, 0x00, 0x33, 0x87, 0xe6, 0x00, 0x83, 0x27, 0x07, 0x00, 0x03, 0x28, 0x47, 0x00, 0x93, 0x08, 0x06, + 0x00, 0x93, 0x82, 0x05, 0x00, 0x6f, 0x00, 0x00, 0x01, 0x93, 0x82, 0x12, 0x00, 0x93, 0x88, 0x08, 0x02, 0xe3, 0x02, + 0x5f, 0xfc, 0x03, 0xa3, 0xc8, 0xfe, 0x83, 0xa3, 0x88, 0xfe, 0x33, 0x43, 0x68, 0x00, 0xb3, 0xc3, 0x77, 0x00, 0x33, + 0xe3, 0x63, 0x00, 0xe3, 0x10, 0x03, 0xfe, 0x03, 0x23, 0x87, 0x00, 0x83, 0x23, 0xc7, 0x00, 0x03, 0xae, 0x48, 0xff, + 0x83, 0xae, 0x08, 0xff, 0xb3, 0xc3, 0xc3, 0x01, 0x33, 0x43, 0xd3, 0x01, 0x33, 0x63, 0x73, 0x00, 0xe3, 0x10, 0x03, + 0xfc, 0x03, 0x23, 0x07, 0x01, 0x83, 0x23, 0x47, 0x01, 0x03, 0xae, 0xc8, 0xff, 0x83, 0xae, 0x88, 0xff, 0xb3, 0xc3, + 0xc3, 0x01, 0x33, 0x43, 0xd3, 0x01, 0x33, 0x63, 0x73, 0x00, 0xe3, 0x10, 0x03, 0xfa, 0x03, 0x23, 0x87, 0x01, 0x83, + 0x23, 0xc7, 0x01, 0x03, 0xae, 0x48, 0x00, 0x83, 0xae, 0x08, 0x00, 0xb3, 0xc3, 0xc3, 0x01, 0x33, 0x43, 0xd3, 0x01, + 0x33, 0x63, 0x73, 0x00, 0xe3, 0x10, 0x03, 0xf8, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x45, 0x34, 0x37, 0xa6, 0x00, + 0x00, 0x13, 0x06, 0x06, 0x38, 0x93, 0x05, 0xc0, 0x03, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xe7, 0x37, 0xa5, + 0x00, 0x00, 0x13, 0x05, 0x05, 0xfe, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x86, 0x00, 0x93, 0x05, 0x80, 0x02, 0x97, + 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xe5, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x39, 0x37, 0xa6, 0x00, 0x00, + 0x13, 0x06, 0x06, 0x3c, 0x93, 0x05, 0x00, 0x03, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xe4, 0x37, 0xa5, 0x00, + 0x00, 0x13, 0x05, 0x05, 0x30, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x33, 0x93, 0x05, 0x40, 0x03, 0x97, 0x40, + 0x00, 0x00, 0xe7, 0x80, 0x40, 0xe2, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x05, 0x08, 0x37, 0xa6, 0x00, 0x00, 0x13, + 0x06, 0x86, 0x0b, 0x93, 0x05, 0x70, 0x03, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xe0, 0x37, 0xa5, 0x00, 0x00, + 0x13, 0x05, 0x85, 0x01, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x04, 0x93, 0x05, 0xa0, 0x02, 0x97, 0x40, 0x00, + 0x00, 0xe7, 0x80, 0xc0, 0xde, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0x85, 0x0c, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, + 0x46, 0x0e, 0x93, 0x05, 0xc0, 0x01, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xdd, 0x37, 0xa5, 0x00, 0x00, 0x13, + 0x05, 0xc5, 0x13, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, 0x15, 0x93, 0x05, 0xf0, 0x01, 0x97, 0x40, 0x00, 0x00, + 0xe7, 0x80, 0x40, 0xdb, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, 0xc5, 0x1f, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, + 0x22, 0x93, 0x05, 0x10, 0x02, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xd9, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, + 0x45, 0x05, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, 0x07, 0x93, 0x05, 0xc0, 0x01, 0x97, 0x40, 0x00, 0x00, 0xe7, + 0x80, 0xc0, 0xd7, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, 0xfd, 0x13, 0x05, 0x00, 0x02, 0x93, 0x05, 0x00, 0x02, + 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xda, 0x33, 0x05, 0xac, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x06, + 0xfc, 0x93, 0x05, 0x00, 0x11, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xd9, 0x37, 0xa5, 0x00, 0x00, 0x13, 0x05, + 0x45, 0x1b, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, 0x1e, 0x93, 0x05, 0x80, 0x03, 0x97, 0x40, 0x00, 0x00, 0xe7, + 0x80, 0x00, 0xd3, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0xe1, 0x17, 0x43, 0x00, 0x00, 0x67, 0x00, 0x43, 0xbd, + 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, 0x11, 0x02, 0x23, 0x24, 0x81, 0x02, 0x23, 0x22, 0x91, 0x02, 0x23, 0x20, 0x21, + 0x03, 0x23, 0x2e, 0x31, 0x01, 0x23, 0x2c, 0x41, 0x01, 0x23, 0x2a, 0x51, 0x01, 0x23, 0x28, 0x61, 0x01, 0x23, 0x26, + 0x71, 0x01, 0x63, 0x0a, 0x06, 0x20, 0x13, 0x84, 0x05, 0x00, 0x93, 0x04, 0x05, 0x00, 0x93, 0x0a, 0xf0, 0xff, 0x13, + 0x09, 0x05, 0x06, 0x93, 0x09, 0x45, 0x08, 0x03, 0x25, 0x05, 0x08, 0x13, 0x8a, 0xc4, 0x09, 0x13, 0x1b, 0x36, 0x00, + 0x93, 0x0b, 0x40, 0x00, 0x6f, 0x00, 0x40, 0x06, 0x23, 0xac, 0xb4, 0x00, 0x23, 0xae, 0xa4, 0x00, 0x13, 0x85, 0x09, + 0x00, 0x93, 0x85, 0x04, 0x00, 0x97, 0x20, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xe2, 0x13, 0x05, 0x0a, 0x00, 0x93, 0x85, + 0x04, 0x00, 0x97, 0x20, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xa3, 0x83, 0xa5, 0x44, 0x09, 0x03, 0xa6, 0x84, 0x09, 0x13, + 0x85, 0x04, 0x00, 0x97, 0x10, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xbd, 0x13, 0x06, 0x40, 0x02, 0x13, 0x05, 0x09, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x7f, 0x13, 0x05, 0x00, 0x00, 0x13, 0x0b, 0x8b, + 0xff, 0x13, 0x04, 0x84, 0x00, 0x63, 0x06, 0x0b, 0x18, 0x63, 0x7a, 0x75, 0x1b, 0x83, 0x25, 0x04, 0x00, 0x03, 0x26, + 0x44, 0x00, 0x13, 0x15, 0x35, 0x00, 0x33, 0x05, 0xa9, 0x00, 0x23, 0x20, 0xb5, 0x00, 0x23, 0x22, 0xc5, 0x00, 0x03, + 0xa5, 0x04, 0x08, 0x13, 0x05, 0x15, 0x00, 0x23, 0xa0, 0xa4, 0x08, 0xe3, 0x16, 0x75, 0xfd, 0x03, 0xa5, 0x04, 0x06, + 0x03, 0xa6, 0x44, 0x06, 0x83, 0xa5, 0x44, 0x00, 0x83, 0xa6, 0x04, 0x00, 0x33, 0x86, 0xc5, 0x00, 0x33, 0x85, 0xa6, + 0x00, 0xb3, 0x36, 0xd5, 0x00, 0x33, 0x06, 0xd6, 0x00, 0x63, 0x04, 0xb6, 0x00, 0xb3, 0x36, 0xb6, 0x00, 0xb3, 0x05, + 0xd5, 0x40, 0xb3, 0xb6, 0xa5, 0x00, 0x33, 0x05, 0xd6, 0x00, 0x33, 0x36, 0xc5, 0x00, 0x33, 0xf6, 0xc6, 0x00, 0x63, + 0x0a, 0x06, 0x00, 0x33, 0x86, 0x55, 0x01, 0xb3, 0x35, 0xb6, 0x00, 0x33, 0x05, 0xb5, 0x00, 0x93, 0x05, 0x06, 0x00, + 0x03, 0xa6, 0x84, 0x06, 0x83, 0xa6, 0xc4, 0x06, 0x03, 0xa7, 0xc4, 0x00, 0x83, 0xa7, 0x84, 0x00, 0xb3, 0x06, 0xd7, + 0x00, 0x33, 0x86, 0xc7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0xb3, 0x86, 0xf6, 0x00, 0x23, 0xa0, 0xb4, 0x00, 0x23, 0xa2, + 0xa4, 0x00, 0x63, 0x84, 0xe6, 0x00, 0xb3, 0xb7, 0xe6, 0x00, 0xb3, 0x05, 0xf6, 0x40, 0x33, 0xb6, 0xc5, 0x00, 0x33, + 0x85, 0xc6, 0x00, 0xb3, 0x36, 0xd5, 0x00, 0x33, 0x76, 0xd6, 0x00, 0x63, 0x0a, 0x06, 0x00, 0x33, 0x86, 0x55, 0x01, + 0xb3, 0x35, 0xb6, 0x00, 0x33, 0x05, 0xb5, 0x00, 0x93, 0x05, 0x06, 0x00, 0x03, 0xa6, 0x04, 0x07, 0x83, 0xa6, 0x44, + 0x07, 0x03, 0xa7, 0x44, 0x01, 0x83, 0xa7, 0x04, 0x01, 0xb3, 0x06, 0xd7, 0x00, 0x33, 0x86, 0xc7, 0x00, 0xb3, 0x37, + 0xf6, 0x00, 0xb3, 0x86, 0xf6, 0x00, 0x23, 0xa4, 0xb4, 0x00, 0x23, 0xa6, 0xa4, 0x00, 0x63, 0x84, 0xe6, 0x00, 0xb3, + 0xb7, 0xe6, 0x00, 0xb3, 0x05, 0xf6, 0x40, 0x33, 0xb6, 0xc5, 0x00, 0x33, 0x85, 0xc6, 0x00, 0xb3, 0x36, 0xd5, 0x00, + 0x33, 0x76, 0xd6, 0x00, 0x63, 0x0a, 0x06, 0x00, 0x33, 0x86, 0x55, 0x01, 0xb3, 0x35, 0xb6, 0x00, 0x33, 0x05, 0xb5, + 0x00, 0x93, 0x05, 0x06, 0x00, 0x03, 0xa6, 0x84, 0x07, 0x83, 0xa6, 0xc4, 0x07, 0x03, 0xa7, 0xc4, 0x01, 0x83, 0xa7, + 0x84, 0x01, 0xb3, 0x06, 0xd7, 0x00, 0x33, 0x86, 0xc7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0xb3, 0x86, 0xf6, 0x00, 0x23, + 0xa8, 0xb4, 0x00, 0x23, 0xaa, 0xa4, 0x00, 0x63, 0x84, 0xe6, 0x00, 0xb3, 0xb7, 0xe6, 0x00, 0xb3, 0x05, 0xf6, 0x40, + 0x33, 0xb6, 0xc5, 0x00, 0x33, 0x85, 0xc6, 0x00, 0xb3, 0x36, 0xd5, 0x00, 0x33, 0x76, 0xd6, 0x00, 0xe3, 0x08, 0x06, + 0xe2, 0x33, 0x86, 0x55, 0x01, 0xb3, 0x35, 0xb6, 0x00, 0x33, 0x05, 0xb5, 0x00, 0x93, 0x05, 0x06, 0x00, 0x6f, 0xf0, + 0xdf, 0xe1, 0x83, 0x20, 0xc1, 0x02, 0x03, 0x24, 0x81, 0x02, 0x83, 0x24, 0x41, 0x02, 0x03, 0x29, 0x01, 0x02, 0x83, + 0x29, 0xc1, 0x01, 0x03, 0x2a, 0x81, 0x01, 0x83, 0x2a, 0x41, 0x01, 0x03, 0x2b, 0x01, 0x01, 0x83, 0x2b, 0xc1, 0x00, + 0x13, 0x01, 0x01, 0x03, 0x67, 0x80, 0x00, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x53, 0x93, 0x05, 0x40, + 0x00, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xae, 0x13, 0x01, 0x01, 0xfb, 0x23, 0x26, 0x11, 0x04, 0x23, 0x24, + 0x81, 0x04, 0x23, 0x22, 0x91, 0x04, 0x23, 0x20, 0x21, 0x05, 0x23, 0x2e, 0x31, 0x03, 0x23, 0x2c, 0x41, 0x03, 0x23, + 0x2a, 0x51, 0x03, 0x23, 0x28, 0x61, 0x03, 0x23, 0x26, 0x71, 0x03, 0x23, 0x24, 0x81, 0x03, 0x23, 0x22, 0x91, 0x03, + 0x13, 0x09, 0x06, 0x00, 0x13, 0x66, 0xc6, 0xff, 0xb3, 0x04, 0xc9, 0x40, 0x63, 0xca, 0x04, 0x30, 0x13, 0x04, 0x05, + 0x00, 0x63, 0x8e, 0x04, 0x04, 0x93, 0x8b, 0x05, 0x00, 0x37, 0xb5, 0x02, 0x00, 0x03, 0x40, 0x55, 0x91, 0x93, 0x05, + 0x10, 0x00, 0x13, 0x0a, 0x10, 0x00, 0x13, 0x85, 0x04, 0x00, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0xc0, 0xa7, 0x63, + 0x06, 0x05, 0x2e, 0x93, 0x09, 0x05, 0x00, 0x13, 0xdb, 0x24, 0x00, 0x23, 0x26, 0x91, 0x00, 0x23, 0x28, 0xa1, 0x00, + 0x23, 0x2a, 0x01, 0x00, 0x13, 0xd5, 0xe4, 0x01, 0x13, 0x1a, 0x3b, 0x00, 0x63, 0x12, 0x05, 0x2e, 0x13, 0x05, 0x40, + 0x00, 0x63, 0xf8, 0xa4, 0x02, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x05, 0x80, 0x00, 0x6f, 0x00, 0x40, 0x04, 0x13, 0x0a, + 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x93, 0x09, 0x10, 0x00, 0x23, 0x26, 0x01, 0x00, 0x23, 0x28, 0x31, 0x01, 0x23, + 0x2a, 0x01, 0x00, 0x13, 0x05, 0x80, 0x00, 0x6f, 0x00, 0xc0, 0x02, 0x37, 0xb5, 0x02, 0x00, 0x03, 0x40, 0x55, 0x91, + 0x93, 0x05, 0x80, 0x00, 0x93, 0x0a, 0x80, 0x00, 0x13, 0x05, 0x0a, 0x00, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0x80, + 0xa0, 0x63, 0x0a, 0x05, 0x28, 0x13, 0x8a, 0x04, 0x00, 0x93, 0x85, 0x0b, 0x00, 0x93, 0x0a, 0x00, 0x00, 0x23, 0x2c, + 0x61, 0x01, 0x23, 0x2e, 0xa1, 0x00, 0x23, 0x20, 0x01, 0x02, 0x63, 0x6a, 0x2a, 0x0d, 0x33, 0x85, 0x59, 0x01, 0x13, + 0x06, 0x09, 0x00, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x4e, 0x33, 0x89, 0x2a, 0x01, 0x23, 0x2a, 0x21, 0x01, + 0x63, 0x02, 0x49, 0x11, 0xb3, 0x89, 0x29, 0x01, 0x13, 0x05, 0x10, 0x00, 0x13, 0x09, 0x19, 0x00, 0x23, 0x80, 0xa9, + 0x00, 0x23, 0x2a, 0x21, 0x01, 0x63, 0x6e, 0x99, 0x10, 0x13, 0xf5, 0xc4, 0xff, 0x23, 0x2a, 0x91, 0x00, 0x63, 0x00, + 0x05, 0x16, 0x93, 0x09, 0x00, 0x00, 0x93, 0x04, 0x00, 0x00, 0x03, 0x2a, 0x01, 0x01, 0x13, 0x05, 0xc5, 0xff, 0x13, + 0x75, 0xc5, 0xff, 0x33, 0x05, 0x45, 0x01, 0x93, 0x0a, 0x45, 0x00, 0x37, 0xa9, 0x00, 0x00, 0x13, 0x09, 0x49, 0x4b, + 0x6f, 0x00, 0x00, 0x04, 0x93, 0x9c, 0x8c, 0x01, 0x13, 0x1c, 0x0c, 0x01, 0x93, 0x9b, 0x8b, 0x00, 0x33, 0xe5, 0x6b, + 0x01, 0x83, 0x25, 0xc1, 0x01, 0x93, 0x84, 0x14, 0x00, 0x13, 0x0a, 0x4a, 0x00, 0x33, 0x66, 0x9c, 0x01, 0x33, 0x65, + 0xc5, 0x00, 0xb3, 0x85, 0x35, 0x01, 0x23, 0xa0, 0xa5, 0x00, 0x23, 0xa2, 0x05, 0x00, 0x23, 0x20, 0x91, 0x02, 0x93, + 0x89, 0x89, 0x00, 0x63, 0x00, 0x5a, 0x11, 0x03, 0x25, 0x81, 0x01, 0x03, 0x4b, 0x0a, 0x00, 0x83, 0x4b, 0x1a, 0x00, + 0x03, 0x4c, 0x2a, 0x00, 0x83, 0x4c, 0x3a, 0x00, 0xe3, 0x98, 0xa4, 0xfa, 0x13, 0x05, 0x81, 0x01, 0x93, 0x05, 0x09, + 0x00, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x93, 0x6f, 0xf0, 0xdf, 0xf9, 0x13, 0x05, 0xc1, 0x00, 0x93, 0x06, + 0x10, 0x00, 0x13, 0x07, 0x10, 0x00, 0x93, 0x89, 0x05, 0x00, 0x93, 0x05, 0x00, 0x00, 0x13, 0x06, 0x09, 0x00, 0x97, + 0x30, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x9e, 0x93, 0x85, 0x09, 0x00, 0x03, 0x2a, 0xc1, 0x00, 0x83, 0x29, 0x01, 0x01, + 0x83, 0x2a, 0x41, 0x01, 0x33, 0x85, 0x59, 0x01, 0x13, 0x06, 0x09, 0x00, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, + 0x3e, 0x33, 0x89, 0x2a, 0x01, 0x23, 0x2a, 0x21, 0x01, 0xe3, 0x12, 0x49, 0xf1, 0xb7, 0xa5, 0x00, 0x00, 0x93, 0x85, + 0x45, 0x4a, 0x13, 0x05, 0xc1, 0x00, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x55, 0x83, 0x29, 0x01, 0x01, 0xb3, + 0x89, 0x29, 0x01, 0x13, 0x05, 0x10, 0x00, 0x13, 0x09, 0x19, 0x00, 0x23, 0x80, 0xa9, 0x00, 0x23, 0x2a, 0x21, 0x01, + 0xe3, 0x76, 0x99, 0xee, 0x03, 0x25, 0xc1, 0x00, 0xb3, 0x84, 0x24, 0x41, 0x33, 0x05, 0x25, 0x41, 0x63, 0x64, 0x95, + 0x0c, 0x83, 0x29, 0x01, 0x01, 0x93, 0x05, 0x20, 0x00, 0x33, 0x85, 0x29, 0x01, 0x63, 0xe0, 0xb4, 0x02, 0x93, 0x84, + 0xf4, 0xff, 0x93, 0x05, 0x00, 0x00, 0x13, 0x86, 0x04, 0x00, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x00, 0x3a, 0x33, + 0x09, 0x99, 0x00, 0x33, 0x85, 0x29, 0x01, 0x23, 0x00, 0x05, 0x00, 0x93, 0x04, 0x19, 0x00, 0x13, 0xf5, 0xc4, 0xff, + 0x23, 0x2a, 0x91, 0x00, 0xe3, 0x14, 0x05, 0xea, 0x83, 0x24, 0x01, 0x02, 0x83, 0x25, 0xc1, 0x00, 0x83, 0x29, 0x81, + 0x01, 0x03, 0x29, 0xc1, 0x01, 0x63, 0x8a, 0x05, 0x00, 0x03, 0x25, 0x01, 0x01, 0x13, 0x06, 0x10, 0x00, 0x97, 0xd0, + 0xff, 0xff, 0xe7, 0x80, 0x00, 0x87, 0x13, 0x05, 0x04, 0x00, 0x93, 0x05, 0x09, 0x00, 0x13, 0x86, 0x04, 0x00, 0x97, + 0x00, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xab, 0x63, 0x8c, 0x09, 0x00, 0x93, 0x95, 0x39, 0x00, 0x13, 0x06, 0x80, 0x00, + 0x13, 0x05, 0x09, 0x00, 0x97, 0xd0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0x84, 0x83, 0x20, 0xc1, 0x04, 0x03, 0x24, 0x81, + 0x04, 0x83, 0x24, 0x41, 0x04, 0x03, 0x29, 0x01, 0x04, 0x83, 0x29, 0xc1, 0x03, 0x03, 0x2a, 0x81, 0x03, 0x83, 0x2a, + 0x41, 0x03, 0x03, 0x2b, 0x01, 0x03, 0x83, 0x2b, 0xc1, 0x02, 0x03, 0x2c, 0x81, 0x02, 0x83, 0x2c, 0x41, 0x02, 0x13, + 0x01, 0x01, 0x05, 0x67, 0x80, 0x00, 0x00, 0x13, 0x05, 0xc1, 0x00, 0x93, 0x06, 0x10, 0x00, 0x13, 0x07, 0x10, 0x00, + 0x93, 0x05, 0x09, 0x00, 0x13, 0x86, 0x04, 0x00, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x89, 0x03, 0x29, 0x41, + 0x01, 0x83, 0x29, 0x01, 0x01, 0x93, 0x05, 0x20, 0x00, 0x33, 0x85, 0x29, 0x01, 0xe3, 0xf0, 0xb4, 0xf2, 0x6f, 0xf0, + 0x9f, 0xf3, 0x13, 0x0a, 0x00, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x48, 0x13, 0x05, 0x0a, 0x00, 0x93, + 0x85, 0x04, 0x00, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0x00, 0x5a, 0x93, 0x0a, 0x00, 0x00, 0x37, 0xa6, 0x00, 0x00, + 0x13, 0x06, 0x46, 0x49, 0x13, 0x85, 0x0a, 0x00, 0x93, 0x05, 0x0a, 0x00, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0x40, + 0x58, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x23, 0x22, 0x91, 0x00, 0x23, 0x20, + 0x21, 0x01, 0x13, 0x84, 0x05, 0x00, 0x83, 0xa6, 0x05, 0x08, 0x93, 0x05, 0x30, 0x00, 0x63, 0xe6, 0xd5, 0x40, 0x93, + 0x04, 0x04, 0x06, 0x93, 0x96, 0x36, 0x00, 0x93, 0x05, 0x10, 0x00, 0xb3, 0x86, 0xd4, 0x00, 0x23, 0xa0, 0xb6, 0x00, + 0x23, 0xa2, 0x06, 0x00, 0x03, 0x26, 0x04, 0x08, 0x93, 0x06, 0x16, 0x00, 0x93, 0x05, 0xf0, 0xff, 0x23, 0x20, 0xd4, + 0x08, 0x63, 0x00, 0xb6, 0x34, 0x93, 0x05, 0x30, 0x00, 0x63, 0x1c, 0xb6, 0x14, 0x83, 0x25, 0x04, 0x06, 0x83, 0x26, + 0x44, 0x06, 0x03, 0x26, 0x44, 0x00, 0x03, 0x27, 0x04, 0x00, 0xb3, 0x07, 0xd6, 0x00, 0xb3, 0x05, 0xb7, 0x00, 0xb3, + 0xb6, 0xe5, 0x00, 0x33, 0x87, 0xd7, 0x00, 0x63, 0x04, 0xc7, 0x00, 0xb3, 0x36, 0xc7, 0x00, 0xb3, 0x86, 0xd5, 0x40, + 0xb3, 0xb5, 0xb6, 0x00, 0x33, 0x06, 0xb7, 0x00, 0x33, 0x37, 0xe6, 0x00, 0x33, 0xf7, 0xe5, 0x00, 0x93, 0x05, 0xf0, + 0xff, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x87, 0xb6, 0x00, 0xb3, 0x36, 0xd7, 0x00, 0x33, 0x06, 0xd6, 0x00, 0x93, 0x06, + 0x07, 0x00, 0x03, 0x27, 0x84, 0x06, 0x83, 0x27, 0xc4, 0x06, 0x03, 0x28, 0xc4, 0x00, 0x83, 0x28, 0x84, 0x00, 0xb3, + 0x07, 0xf8, 0x00, 0x33, 0x87, 0xe8, 0x00, 0xb3, 0x38, 0x17, 0x01, 0xb3, 0x87, 0x17, 0x01, 0x23, 0x20, 0xd4, 0x00, + 0x23, 0x22, 0xc4, 0x00, 0x63, 0x84, 0x07, 0x01, 0xb3, 0xb8, 0x07, 0x01, 0xb3, 0x06, 0x17, 0x41, 0x33, 0xb7, 0xe6, + 0x00, 0x33, 0x86, 0xe7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x87, + 0xb6, 0x00, 0xb3, 0x36, 0xd7, 0x00, 0x33, 0x06, 0xd6, 0x00, 0x93, 0x06, 0x07, 0x00, 0x03, 0x27, 0x04, 0x07, 0x83, + 0x27, 0x44, 0x07, 0x03, 0x28, 0x44, 0x01, 0x83, 0x28, 0x04, 0x01, 0xb3, 0x07, 0xf8, 0x00, 0x33, 0x87, 0xe8, 0x00, + 0xb3, 0x38, 0x17, 0x01, 0xb3, 0x87, 0x17, 0x01, 0x23, 0x24, 0xd4, 0x00, 0x23, 0x26, 0xc4, 0x00, 0x63, 0x84, 0x07, + 0x01, 0xb3, 0xb8, 0x07, 0x01, 0xb3, 0x06, 0x17, 0x41, 0x33, 0xb7, 0xe6, 0x00, 0x33, 0x86, 0xe7, 0x00, 0xb3, 0x37, + 0xf6, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x87, 0xb6, 0x00, 0xb3, 0x36, 0xd7, 0x00, 0x33, + 0x06, 0xd6, 0x00, 0x93, 0x06, 0x07, 0x00, 0x03, 0x27, 0x84, 0x07, 0x83, 0x27, 0xc4, 0x07, 0x03, 0x28, 0xc4, 0x01, + 0x83, 0x28, 0x84, 0x01, 0xb3, 0x07, 0xf8, 0x00, 0x33, 0x87, 0xe8, 0x00, 0xb3, 0x38, 0x17, 0x01, 0xb3, 0x87, 0x17, + 0x01, 0x23, 0x28, 0xd4, 0x00, 0x23, 0x2a, 0xc4, 0x00, 0x63, 0x84, 0x07, 0x01, 0xb3, 0xb8, 0x07, 0x01, 0xb3, 0x06, + 0x17, 0x41, 0x33, 0xb7, 0xe6, 0x00, 0x33, 0x86, 0xe7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0x13, 0x09, 0x05, 0x00, 0x33, + 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x18, 0x6f, 0x00, 0x00, 0x18, 0x13, 0x06, 0xf0, 0xff, 0x63, 0xee, 0xd5, 0x26, + 0x93, 0x96, 0x36, 0x00, 0xb3, 0x86, 0xd4, 0x00, 0x23, 0xa0, 0x06, 0x00, 0x23, 0xa2, 0x06, 0x00, 0x03, 0x27, 0x04, + 0x08, 0x93, 0x06, 0x17, 0x00, 0x23, 0x20, 0xd4, 0x08, 0x63, 0x0e, 0xc7, 0x1a, 0xe3, 0x1e, 0xb7, 0xfc, 0x83, 0x25, + 0x04, 0x06, 0x83, 0x26, 0x44, 0x06, 0x03, 0x26, 0x44, 0x00, 0x03, 0x27, 0x04, 0x00, 0xb3, 0x07, 0xd6, 0x00, 0xb3, + 0x05, 0xb7, 0x00, 0xb3, 0xb6, 0xe5, 0x00, 0x33, 0x87, 0xd7, 0x00, 0x63, 0x04, 0xc7, 0x00, 0xb3, 0x36, 0xc7, 0x00, + 0xb3, 0x86, 0xd5, 0x40, 0xb3, 0xb5, 0xb6, 0x00, 0x33, 0x06, 0xb7, 0x00, 0x33, 0x37, 0xe6, 0x00, 0x33, 0xf7, 0xe5, + 0x00, 0x93, 0x05, 0xf0, 0xff, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x87, 0xb6, 0x00, 0xb3, 0x36, 0xd7, 0x00, 0x33, 0x06, + 0xd6, 0x00, 0x93, 0x06, 0x07, 0x00, 0x03, 0x27, 0x84, 0x06, 0x83, 0x27, 0xc4, 0x06, 0x03, 0x28, 0xc4, 0x00, 0x83, + 0x28, 0x84, 0x00, 0xb3, 0x07, 0xf8, 0x00, 0x33, 0x87, 0xe8, 0x00, 0xb3, 0x38, 0x17, 0x01, 0xb3, 0x87, 0x17, 0x01, + 0x23, 0x20, 0xd4, 0x00, 0x23, 0x22, 0xc4, 0x00, 0x63, 0x84, 0x07, 0x01, 0xb3, 0xb8, 0x07, 0x01, 0xb3, 0x06, 0x17, + 0x41, 0x33, 0xb7, 0xe6, 0x00, 0x33, 0x86, 0xe7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, + 0x07, 0x00, 0x33, 0x87, 0xb6, 0x00, 0xb3, 0x36, 0xd7, 0x00, 0x33, 0x06, 0xd6, 0x00, 0x93, 0x06, 0x07, 0x00, 0x03, + 0x27, 0x04, 0x07, 0x83, 0x27, 0x44, 0x07, 0x03, 0x28, 0x44, 0x01, 0x83, 0x28, 0x04, 0x01, 0xb3, 0x07, 0xf8, 0x00, + 0x33, 0x87, 0xe8, 0x00, 0xb3, 0x38, 0x17, 0x01, 0xb3, 0x87, 0x17, 0x01, 0x23, 0x24, 0xd4, 0x00, 0x23, 0x26, 0xc4, + 0x00, 0x63, 0x84, 0x07, 0x01, 0xb3, 0xb8, 0x07, 0x01, 0xb3, 0x06, 0x17, 0x41, 0x33, 0xb7, 0xe6, 0x00, 0x33, 0x86, + 0xe7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x87, 0xb6, 0x00, 0xb3, + 0x36, 0xd7, 0x00, 0x33, 0x06, 0xd6, 0x00, 0x93, 0x06, 0x07, 0x00, 0x03, 0x27, 0x84, 0x07, 0x83, 0x27, 0xc4, 0x07, + 0x03, 0x28, 0xc4, 0x01, 0x83, 0x28, 0x84, 0x01, 0xb3, 0x07, 0xf8, 0x00, 0x33, 0x87, 0xe8, 0x00, 0xb3, 0x38, 0x17, + 0x01, 0xb3, 0x87, 0x17, 0x01, 0x23, 0x28, 0xd4, 0x00, 0x23, 0x2a, 0xc4, 0x00, 0x63, 0x84, 0x07, 0x01, 0xb3, 0xb8, + 0x07, 0x01, 0xb3, 0x06, 0x17, 0x41, 0x33, 0xb7, 0xe6, 0x00, 0x33, 0x86, 0xe7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0x13, + 0x09, 0x05, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0xb3, 0x85, 0xb6, 0x00, 0x33, 0xb5, 0xd5, 0x00, + 0x33, 0x06, 0xa6, 0x00, 0x93, 0x86, 0x05, 0x00, 0x23, 0x2c, 0xd4, 0x00, 0x23, 0x2e, 0xc4, 0x00, 0x13, 0x05, 0x44, + 0x08, 0x93, 0x05, 0x04, 0x00, 0x97, 0x10, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x53, 0x13, 0x05, 0xc4, 0x09, 0x93, 0x05, + 0x04, 0x00, 0x97, 0x10, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x15, 0x83, 0x25, 0x44, 0x09, 0x03, 0x26, 0x84, 0x09, 0x13, + 0x05, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x2e, 0x13, 0x06, 0x40, 0x02, 0x13, 0x85, 0x04, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xf0, 0x13, 0x05, 0x09, 0x00, 0x13, 0x06, 0x00, + 0x06, 0x93, 0x05, 0x04, 0x00, 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xed, 0x83, 0x25, 0x44, 0x08, 0x63, 0x80, + 0x05, 0x02, 0x03, 0x25, 0x84, 0x08, 0x13, 0x96, 0x55, 0x00, 0x93, 0x95, 0x75, 0x00, 0xb3, 0x85, 0xc5, 0x40, 0x13, + 0x06, 0x80, 0x00, 0x97, 0xc0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0x3e, 0x83, 0x25, 0x04, 0x09, 0x63, 0x80, 0x05, 0x02, + 0x03, 0x25, 0x44, 0x09, 0x13, 0x96, 0x55, 0x00, 0x93, 0x95, 0x75, 0x00, 0xb3, 0x85, 0xc5, 0x40, 0x13, 0x06, 0x80, + 0x00, 0x97, 0xc0, 0xff, 0xff, 0xe7, 0x80, 0x00, 0x3c, 0x83, 0x25, 0xc4, 0x09, 0x63, 0x86, 0x05, 0x02, 0x03, 0x25, + 0x04, 0x0a, 0x93, 0x95, 0x35, 0x00, 0x13, 0x06, 0x80, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x03, 0x24, 0x81, 0x00, 0x83, + 0x24, 0x41, 0x00, 0x03, 0x29, 0x01, 0x00, 0x13, 0x01, 0x01, 0x01, 0x17, 0xc3, 0xff, 0xff, 0x67, 0x00, 0x03, 0x39, + 0x83, 0x20, 0xc1, 0x00, 0x03, 0x24, 0x81, 0x00, 0x83, 0x24, 0x41, 0x00, 0x03, 0x29, 0x01, 0x00, 0x13, 0x01, 0x01, + 0x01, 0x67, 0x80, 0x00, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x53, 0x93, 0x05, 0x40, 0x00, 0x13, 0x85, + 0x06, 0x00, 0x97, 0x30, 0x00, 0x00, 0xe7, 0x80, 0x00, 0x32, 0x13, 0x01, 0x01, 0xe3, 0x23, 0x26, 0x11, 0x1c, 0x23, + 0x24, 0x81, 0x1c, 0x23, 0x22, 0x91, 0x1c, 0x23, 0x20, 0x21, 0x1d, 0x23, 0x2e, 0x31, 0x1b, 0x23, 0x2c, 0x41, 0x1b, + 0x93, 0x04, 0x06, 0x00, 0x13, 0x89, 0x05, 0x00, 0x13, 0x04, 0x05, 0x00, 0x13, 0x05, 0xc1, 0x08, 0x97, 0x20, 0x00, + 0x00, 0xe7, 0x80, 0x80, 0x4e, 0x13, 0x05, 0x81, 0x00, 0x13, 0x06, 0x40, 0x08, 0x93, 0x05, 0x00, 0x00, 0x97, 0x40, + 0x00, 0x00, 0xe7, 0x80, 0x80, 0xe0, 0x13, 0x05, 0x81, 0x00, 0x93, 0x05, 0x09, 0x00, 0x13, 0x86, 0x04, 0x00, 0x97, + 0xf0, 0xff, 0xff, 0xe7, 0x80, 0x00, 0x7e, 0x13, 0x05, 0x01, 0x0b, 0x93, 0x05, 0x81, 0x00, 0x13, 0x06, 0x80, 0x0a, + 0x97, 0x40, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xdb, 0x13, 0x05, 0x81, 0x15, 0x93, 0x05, 0x01, 0x0b, 0x97, 0x00, 0x00, + 0x00, 0xe7, 0x80, 0x40, 0xb4, 0x83, 0x22, 0x81, 0x15, 0x83, 0x25, 0xc1, 0x15, 0x03, 0x23, 0x01, 0x16, 0x03, 0x26, + 0x41, 0x16, 0x83, 0x23, 0x81, 0x16, 0x83, 0x26, 0xc1, 0x16, 0x03, 0x2e, 0x01, 0x17, 0x03, 0x25, 0x41, 0x17, 0x33, + 0x37, 0x70, 0x00, 0x93, 0x87, 0x16, 0x00, 0x33, 0x38, 0x60, 0x00, 0x93, 0x08, 0x16, 0x00, 0xb3, 0x3e, 0x50, 0x00, + 0x13, 0x8f, 0x15, 0x00, 0xb3, 0x3f, 0xc0, 0x01, 0x93, 0x04, 0x15, 0x00, 0x93, 0xb7, 0x17, 0x00, 0x93, 0xb8, 0x18, + 0x00, 0x13, 0x3f, 0x1f, 0x00, 0x93, 0xb4, 0x14, 0x00, 0x33, 0xf7, 0xe7, 0x00, 0x33, 0xf8, 0x08, 0x01, 0xb3, 0x78, + 0xdf, 0x01, 0xb3, 0xfe, 0xf4, 0x01, 0xb3, 0x87, 0xe3, 0x40, 0x33, 0x08, 0x03, 0x41, 0xb3, 0x88, 0x12, 0x41, 0x33, + 0x07, 0xde, 0x41, 0xb3, 0xb3, 0x77, 0x00, 0x33, 0x33, 0x68, 0x00, 0xb3, 0xb2, 0x58, 0x00, 0x33, 0x3e, 0xc7, 0x01, + 0x93, 0xde, 0x88, 0x01, 0x13, 0xdf, 0x08, 0x01, 0x93, 0xdf, 0x88, 0x00, 0x93, 0x54, 0x88, 0x01, 0x13, 0x59, 0x08, + 0x01, 0x93, 0x59, 0x88, 0x00, 0x13, 0xda, 0x87, 0x01, 0xb3, 0x86, 0x76, 0x00, 0x93, 0xd3, 0x07, 0x01, 0x33, 0x06, + 0x66, 0x00, 0x13, 0xd3, 0x87, 0x00, 0xb3, 0x85, 0x55, 0x00, 0x93, 0x52, 0x87, 0x01, 0x33, 0x05, 0xc5, 0x01, 0x13, + 0x5e, 0x07, 0x01, 0x23, 0x00, 0x14, 0x01, 0xa3, 0x00, 0xf4, 0x01, 0x23, 0x01, 0xe4, 0x01, 0xa3, 0x01, 0xd4, 0x01, + 0x93, 0x58, 0x87, 0x00, 0x23, 0x04, 0x04, 0x01, 0xa3, 0x04, 0x34, 0x01, 0x23, 0x05, 0x24, 0x01, 0xa3, 0x05, 0x94, + 0x00, 0x23, 0x08, 0xf4, 0x00, 0xa3, 0x08, 0x64, 0x00, 0x23, 0x09, 0x74, 0x00, 0xa3, 0x09, 0x44, 0x01, 0x23, 0x0c, + 0xe4, 0x00, 0xa3, 0x0c, 0x14, 0x01, 0x23, 0x0d, 0xc4, 0x01, 0xa3, 0x0d, 0x54, 0x00, 0x13, 0xd7, 0x85, 0x01, 0x93, + 0xd7, 0x05, 0x01, 0x13, 0xd8, 0x85, 0x00, 0x93, 0x58, 0x86, 0x01, 0x93, 0x52, 0x06, 0x01, 0x13, 0x53, 0x86, 0x00, + 0x93, 0xd3, 0x86, 0x01, 0x13, 0xde, 0x06, 0x01, 0x93, 0xde, 0x86, 0x00, 0x13, 0x5f, 0x85, 0x01, 0x93, 0x5f, 0x05, + 0x01, 0x23, 0x02, 0xb4, 0x00, 0xa3, 0x02, 0x04, 0x01, 0x23, 0x03, 0xf4, 0x00, 0xa3, 0x03, 0xe4, 0x00, 0x93, 0x55, + 0x85, 0x00, 0x23, 0x06, 0xc4, 0x00, 0xa3, 0x06, 0x64, 0x00, 0x23, 0x07, 0x54, 0x00, 0xa3, 0x07, 0x14, 0x01, 0x23, + 0x0a, 0xd4, 0x00, 0xa3, 0x0a, 0xd4, 0x01, 0x23, 0x0b, 0xc4, 0x01, 0xa3, 0x0b, 0x74, 0x00, 0x23, 0x0e, 0xa4, 0x00, + 0xa3, 0x0e, 0xb4, 0x00, 0x23, 0x0f, 0xf4, 0x01, 0xa3, 0x0f, 0xe4, 0x01, 0x83, 0x20, 0xc1, 0x1c, 0x03, 0x24, 0x81, + 0x1c, 0x83, 0x24, 0x41, 0x1c, 0x03, 0x29, 0x01, 0x1c, 0x83, 0x29, 0xc1, 0x1b, 0x03, 0x2a, 0x81, 0x1b, 0x13, 0x01, + 0x01, 0x1d, 0x67, 0x80, 0x00, 0x00, 0xe3, 0x0a, 0x06, 0x64, 0x13, 0x01, 0x01, 0xf8, 0x23, 0x2e, 0x11, 0x06, 0x23, + 0x2c, 0x81, 0x06, 0x23, 0x2a, 0x91, 0x06, 0x23, 0x28, 0x21, 0x07, 0x23, 0x26, 0x31, 0x07, 0x23, 0x24, 0x41, 0x07, + 0x23, 0x22, 0x51, 0x07, 0x23, 0x20, 0x61, 0x07, 0x23, 0x2e, 0x71, 0x05, 0x23, 0x2c, 0x81, 0x05, 0x23, 0x2a, 0x91, + 0x05, 0x23, 0x28, 0xa1, 0x05, 0x23, 0x26, 0xb1, 0x05, 0x93, 0x17, 0x56, 0x00, 0x13, 0x17, 0x76, 0x00, 0x13, 0x06, + 0xf0, 0xff, 0x93, 0x06, 0x10, 0x00, 0x33, 0x07, 0xf7, 0x40, 0x33, 0x87, 0xe5, 0x00, 0x23, 0x24, 0xe1, 0x00, 0x93, + 0x07, 0x05, 0x06, 0x6f, 0x00, 0xc0, 0x01, 0x83, 0x25, 0x81, 0x04, 0x93, 0x85, 0x05, 0x06, 0x23, 0x2c, 0xe5, 0x04, + 0x23, 0x2e, 0x55, 0x04, 0x03, 0x27, 0x81, 0x00, 0xe3, 0x84, 0xe5, 0x5a, 0x23, 0x24, 0xb1, 0x04, 0x13, 0x88, 0x05, + 0x00, 0x93, 0x08, 0x05, 0x00, 0x6f, 0x00, 0x80, 0x02, 0x33, 0x07, 0x60, 0x40, 0xb3, 0x85, 0x65, 0x40, 0x33, 0xb7, + 0xe5, 0x00, 0x33, 0x87, 0xe2, 0x00, 0x23, 0xa0, 0xb8, 0x00, 0x23, 0xa2, 0xe8, 0x00, 0x93, 0x88, 0x88, 0x00, 0x13, + 0x08, 0x88, 0x00, 0x63, 0x8c, 0xf8, 0x32, 0x03, 0x27, 0x08, 0x00, 0x83, 0x22, 0x48, 0x00, 0x83, 0xa5, 0x48, 0x00, + 0x03, 0xa3, 0x08, 0x00, 0xb3, 0x82, 0x55, 0x00, 0x33, 0x07, 0xe3, 0x00, 0x33, 0x33, 0x67, 0x00, 0xb3, 0x82, 0x62, + 0x00, 0x63, 0x84, 0xb2, 0x00, 0x33, 0xb3, 0xb2, 0x00, 0xb3, 0x05, 0x67, 0x40, 0x33, 0xb3, 0xe5, 0x00, 0x33, 0x87, + 0x62, 0x00, 0xb3, 0x32, 0x57, 0x00, 0xb3, 0x72, 0x53, 0x00, 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x82, 0xc5, 0x00, 0xb3, + 0xb5, 0xb2, 0x00, 0x33, 0x07, 0xb7, 0x00, 0x93, 0x85, 0x02, 0x00, 0xb3, 0x02, 0xb7, 0x02, 0x33, 0xb3, 0xb5, 0x02, + 0xb3, 0x33, 0xb7, 0x02, 0x33, 0x0e, 0xe7, 0x02, 0x33, 0x83, 0x62, 0x00, 0xb3, 0x3e, 0x53, 0x00, 0x33, 0x83, 0x62, + 0x00, 0xb3, 0x8e, 0xd3, 0x01, 0xb3, 0x32, 0x53, 0x00, 0xb3, 0x82, 0x53, 0x00, 0xb3, 0x33, 0xe7, 0x02, 0xb3, 0x82, + 0x5e, 0x00, 0xb3, 0xbe, 0xd2, 0x01, 0xb3, 0x02, 0x5e, 0x00, 0x33, 0xbe, 0xc2, 0x01, 0xb3, 0x83, 0xd3, 0x01, 0xb3, + 0x83, 0xc3, 0x01, 0x33, 0x8e, 0xb5, 0x02, 0xb3, 0x3e, 0x7e, 0x00, 0x63, 0x0c, 0x03, 0x00, 0x13, 0x0f, 0x10, 0x00, + 0xb3, 0x03, 0x7e, 0x40, 0x33, 0x03, 0xd3, 0x41, 0x63, 0x0c, 0x0f, 0x00, 0x6f, 0x00, 0x80, 0x02, 0x13, 0xcf, 0x1e, + 0x00, 0xb3, 0x03, 0x7e, 0x40, 0x33, 0x03, 0xd3, 0x41, 0x63, 0x1c, 0x0f, 0x00, 0x33, 0x8e, 0xd3, 0x00, 0xb3, 0x33, + 0x7e, 0x00, 0x33, 0x03, 0xc3, 0x00, 0x33, 0x03, 0x73, 0x00, 0x93, 0x03, 0x0e, 0x00, 0xb3, 0xf2, 0xc2, 0x00, 0x33, + 0xbe, 0xc2, 0x02, 0xb3, 0x82, 0x53, 0x40, 0xb3, 0x0e, 0x6e, 0x00, 0x33, 0xbe, 0x72, 0x00, 0xb3, 0x83, 0xce, 0x01, + 0x63, 0x84, 0x63, 0x00, 0x33, 0xbe, 0x63, 0x00, 0x33, 0x03, 0xc0, 0x41, 0xb3, 0x82, 0xc2, 0x41, 0x33, 0xb3, 0x62, + 0x00, 0x33, 0xbe, 0xb2, 0x02, 0xb3, 0x8e, 0xe2, 0x02, 0x33, 0x83, 0x63, 0x00, 0xb3, 0x03, 0xb3, 0x02, 0x33, 0x3f, + 0xb3, 0x02, 0x33, 0x8e, 0xc3, 0x01, 0xb3, 0x33, 0x7e, 0x00, 0x33, 0x0f, 0x7f, 0x00, 0xb3, 0xbf, 0xe2, 0x02, 0xb3, + 0x83, 0xce, 0x01, 0x33, 0xbe, 0xd3, 0x01, 0x33, 0x8e, 0xcf, 0x01, 0xb3, 0x3e, 0xe3, 0x02, 0xb3, 0x0f, 0xe3, 0x02, + 0x33, 0x07, 0xcf, 0x01, 0x33, 0x3e, 0xe7, 0x01, 0x33, 0x87, 0xef, 0x00, 0x33, 0x8e, 0xce, 0x01, 0xb3, 0x3e, 0xf7, + 0x01, 0x33, 0x0e, 0xde, 0x01, 0xb3, 0x85, 0xb2, 0x02, 0xb3, 0xbe, 0xc5, 0x01, 0x63, 0x8c, 0x03, 0x00, 0x13, 0x0f, + 0x10, 0x00, 0xb3, 0x85, 0xc5, 0x41, 0x33, 0x8e, 0xd3, 0x41, 0x63, 0x0c, 0x0f, 0x00, 0x6f, 0x00, 0x80, 0x02, 0x13, + 0xcf, 0x1e, 0x00, 0xb3, 0x85, 0xc5, 0x41, 0x33, 0x8e, 0xd3, 0x41, 0x63, 0x1c, 0x0f, 0x00, 0xb3, 0x83, 0xd5, 0x00, + 0xb3, 0xb5, 0xb3, 0x00, 0x33, 0x0e, 0xce, 0x00, 0x33, 0x0e, 0xbe, 0x00, 0x93, 0x85, 0x03, 0x00, 0x33, 0x77, 0xc7, + 0x00, 0xb3, 0x33, 0xc7, 0x02, 0x33, 0x87, 0xe5, 0x40, 0xb3, 0x8e, 0xc3, 0x01, 0xb3, 0x33, 0xb7, 0x00, 0xb3, 0x85, + 0x7e, 0x00, 0x63, 0x84, 0xc5, 0x01, 0xb3, 0xb3, 0xc5, 0x01, 0xb3, 0x0e, 0x53, 0x02, 0x33, 0xbe, 0x52, 0x02, 0x33, + 0x3f, 0x53, 0x02, 0xb3, 0x0f, 0x63, 0x02, 0x33, 0x34, 0x63, 0x02, 0x33, 0x8e, 0xce, 0x01, 0x33, 0x33, 0xde, 0x01, + 0x33, 0x8e, 0xce, 0x01, 0xb3, 0x04, 0x6f, 0x00, 0x33, 0x33, 0xde, 0x01, 0x33, 0x03, 0x6f, 0x00, 0xb3, 0x8e, 0x64, + 0x00, 0x33, 0x83, 0xdf, 0x01, 0xb3, 0xbe, 0x9e, 0x00, 0x33, 0x3f, 0xf3, 0x01, 0xb3, 0x0e, 0xd4, 0x01, 0xb3, 0x8e, + 0xee, 0x01, 0xb3, 0x82, 0x52, 0x02, 0x33, 0xbf, 0xd2, 0x01, 0x63, 0x0c, 0x0e, 0x00, 0x93, 0x0f, 0x10, 0x00, 0xb3, + 0x8e, 0xd2, 0x41, 0xb3, 0x02, 0xee, 0x41, 0x63, 0x8c, 0x0f, 0x00, 0x6f, 0x00, 0x80, 0x02, 0x93, 0x4f, 0x1f, 0x00, + 0xb3, 0x8e, 0xd2, 0x41, 0xb3, 0x02, 0xee, 0x41, 0x63, 0x9c, 0x0f, 0x00, 0x33, 0x8e, 0xde, 0x00, 0xb3, 0x3e, 0xde, + 0x01, 0xb3, 0x82, 0xc2, 0x00, 0xb3, 0x82, 0xd2, 0x01, 0x93, 0x0e, 0x0e, 0x00, 0x33, 0x73, 0xc3, 0x00, 0x33, 0x3f, + 0xc3, 0x02, 0x33, 0x8e, 0x6e, 0x40, 0x33, 0x03, 0x5f, 0x00, 0xb3, 0x3e, 0xde, 0x01, 0x33, 0x03, 0xd3, 0x01, 0x63, + 0x04, 0x53, 0x00, 0xb3, 0x3e, 0x53, 0x00, 0xb3, 0x02, 0x70, 0x40, 0xb3, 0x03, 0x77, 0x40, 0x33, 0x07, 0xd0, 0x41, + 0x33, 0x0e, 0xde, 0x41, 0xb3, 0xb2, 0x53, 0x00, 0x33, 0x37, 0xee, 0x00, 0xb3, 0x3e, 0x7e, 0x02, 0xb3, 0x85, 0x55, + 0x00, 0x33, 0x07, 0xe3, 0x00, 0xb3, 0x02, 0x77, 0x02, 0x33, 0x33, 0x77, 0x02, 0x33, 0x0f, 0xbe, 0x02, 0xb3, 0x8e, + 0xd2, 0x01, 0xb3, 0xb2, 0x5e, 0x00, 0xb3, 0x02, 0x53, 0x00, 0x33, 0x33, 0xb7, 0x02, 0xb3, 0x0f, 0xb7, 0x02, 0xb3, + 0x35, 0xbe, 0x02, 0x33, 0x07, 0xdf, 0x01, 0xb3, 0x3e, 0xe7, 0x01, 0xb3, 0x85, 0xd5, 0x01, 0xb3, 0x85, 0xb2, 0x00, + 0xb3, 0xb2, 0x55, 0x00, 0xb3, 0x85, 0xbf, 0x00, 0xb3, 0x02, 0x53, 0x00, 0x33, 0xb3, 0xf5, 0x01, 0xb3, 0x82, 0x62, + 0x00, 0x33, 0x03, 0x7e, 0x02, 0xb3, 0x33, 0x53, 0x00, 0x63, 0x0c, 0x07, 0x00, 0x13, 0x0e, 0x10, 0x00, 0xb3, 0x02, + 0x53, 0x40, 0x33, 0x07, 0x77, 0x40, 0x63, 0x0c, 0x0e, 0x00, 0x6f, 0x00, 0x80, 0x02, 0x13, 0xce, 0x13, 0x00, 0xb3, + 0x02, 0x53, 0x40, 0x33, 0x07, 0x77, 0x40, 0x63, 0x1c, 0x0e, 0x00, 0x33, 0x83, 0xd2, 0x00, 0xb3, 0x32, 0x53, 0x00, + 0x33, 0x07, 0xc7, 0x00, 0x33, 0x07, 0x57, 0x00, 0x93, 0x02, 0x03, 0x00, 0xb3, 0xf5, 0xc5, 0x00, 0x33, 0xb3, 0xc5, + 0x02, 0xb3, 0x85, 0xb2, 0x40, 0xb3, 0x03, 0xe3, 0x00, 0x33, 0xb3, 0x55, 0x00, 0xb3, 0x82, 0x63, 0x00, 0xe3, 0x8a, + 0xe2, 0xca, 0x33, 0xb3, 0xe2, 0x00, 0x6f, 0xf0, 0xdf, 0xca, 0x13, 0x08, 0x05, 0x00, 0x6f, 0x00, 0x40, 0x01, 0x23, + 0x28, 0xe8, 0x00, 0x23, 0x2a, 0xb8, 0x00, 0x13, 0x08, 0x08, 0x02, 0x63, 0x0a, 0xf8, 0x44, 0x03, 0x2e, 0xc8, 0x00, + 0x83, 0x24, 0x08, 0x00, 0x03, 0x24, 0x48, 0x00, 0x83, 0x2e, 0x88, 0x00, 0xb3, 0x08, 0xc4, 0x01, 0xb3, 0x85, 0xd4, + 0x01, 0x33, 0xb7, 0x95, 0x00, 0xb3, 0x88, 0xe8, 0x00, 0x63, 0x84, 0x88, 0x00, 0x33, 0xb7, 0x88, 0x00, 0x33, 0x87, + 0xe5, 0x40, 0xb3, 0x32, 0xb7, 0x00, 0xb3, 0x85, 0x58, 0x00, 0xb3, 0xb8, 0x15, 0x01, 0xb3, 0xf8, 0x12, 0x01, 0x63, + 0x8a, 0x08, 0x00, 0xb3, 0x08, 0xc7, 0x00, 0x33, 0xb7, 0xe8, 0x00, 0xb3, 0x85, 0xe5, 0x00, 0x13, 0x87, 0x08, 0x00, + 0x03, 0x2f, 0xc8, 0x01, 0x83, 0x23, 0x08, 0x01, 0x03, 0x23, 0x48, 0x01, 0x03, 0x29, 0x88, 0x01, 0xb3, 0x0f, 0xe3, + 0x01, 0xb3, 0x88, 0x23, 0x01, 0xb3, 0xb2, 0x78, 0x00, 0xb3, 0x8f, 0x5f, 0x00, 0x63, 0x9c, 0x6f, 0x1e, 0xb3, 0x82, + 0x58, 0x40, 0xb3, 0xb9, 0x12, 0x01, 0xb3, 0x88, 0x3f, 0x01, 0xb3, 0xbf, 0xf8, 0x01, 0xb3, 0xff, 0xf9, 0x01, 0x63, + 0x9e, 0x0f, 0x1e, 0xb3, 0x0f, 0x57, 0x00, 0xb3, 0xb9, 0xef, 0x00, 0xb3, 0x8a, 0x15, 0x01, 0xb3, 0x8a, 0x3a, 0x01, + 0x63, 0x96, 0xba, 0x20, 0x33, 0x8a, 0x3f, 0x41, 0xb3, 0x3f, 0xfa, 0x01, 0xb3, 0x89, 0xfa, 0x01, 0xb3, 0xba, 0x59, + 0x01, 0xb3, 0xff, 0x5f, 0x01, 0x63, 0x98, 0x0f, 0x20, 0xb3, 0x0e, 0xda, 0x01, 0xb3, 0xbf, 0x4e, 0x01, 0x33, 0x8e, + 0xc9, 0x01, 0x33, 0x0e, 0xfe, 0x01, 0x63, 0x10, 0x3e, 0x23, 0xb3, 0x8f, 0xfe, 0x41, 0xb3, 0xba, 0xdf, 0x01, 0xb3, + 0x0e, 0x5e, 0x01, 0x33, 0xbe, 0xce, 0x01, 0x33, 0xfe, 0xca, 0x01, 0x63, 0x12, 0x0e, 0x22, 0x33, 0x09, 0x2a, 0x01, + 0x33, 0x3e, 0x49, 0x01, 0x33, 0x8f, 0xe9, 0x01, 0x33, 0x0a, 0xcf, 0x01, 0x63, 0x1a, 0x3a, 0x23, 0x33, 0x0f, 0xc9, + 0x41, 0x33, 0x39, 0x2f, 0x01, 0x33, 0x0e, 0x2a, 0x01, 0xb3, 0x39, 0x4e, 0x01, 0x33, 0x79, 0x39, 0x01, 0x63, 0x1c, + 0x09, 0x22, 0x33, 0x89, 0x94, 0x00, 0xb3, 0x34, 0x99, 0x00, 0xb3, 0x09, 0x84, 0x00, 0xb3, 0x89, 0x99, 0x00, 0x63, + 0x94, 0x89, 0x24, 0xb3, 0x04, 0x99, 0x40, 0x33, 0xb9, 0x24, 0x01, 0x33, 0x84, 0x29, 0x01, 0xb3, 0x39, 0x34, 0x01, + 0x33, 0x79, 0x39, 0x01, 0x63, 0x16, 0x09, 0x24, 0x33, 0x09, 0x9f, 0x00, 0xb3, 0x34, 0xe9, 0x01, 0x33, 0x04, 0x8e, + 0x00, 0xb3, 0x09, 0x94, 0x00, 0x63, 0x9c, 0xc9, 0x25, 0xb3, 0x04, 0x99, 0x40, 0x33, 0xb9, 0x24, 0x01, 0x33, 0x84, + 0x29, 0x01, 0xb3, 0x39, 0x34, 0x01, 0x33, 0x79, 0x39, 0x01, 0x63, 0x0a, 0x09, 0x00, 0x33, 0x89, 0xc4, 0x00, 0xb3, + 0x34, 0x99, 0x00, 0x33, 0x04, 0x94, 0x00, 0x93, 0x04, 0x09, 0x00, 0x33, 0x89, 0x73, 0x00, 0xb3, 0x09, 0x63, 0x00, + 0xb3, 0x33, 0x79, 0x00, 0xb3, 0x89, 0x79, 0x00, 0x23, 0x2c, 0x98, 0x00, 0x23, 0x2e, 0x88, 0x00, 0x63, 0x9a, 0x69, + 0x22, 0xb3, 0x03, 0x79, 0x40, 0x33, 0xb4, 0x23, 0x01, 0x33, 0x83, 0x89, 0x00, 0xb3, 0x34, 0x33, 0x01, 0x33, 0x74, + 0x94, 0x00, 0x63, 0x1c, 0x04, 0x22, 0x33, 0x84, 0x7f, 0x00, 0xb3, 0x33, 0xf4, 0x01, 0x33, 0x83, 0x6e, 0x00, 0xb3, + 0x04, 0x73, 0x00, 0x63, 0x92, 0xd4, 0x25, 0xb3, 0x03, 0x74, 0x40, 0x33, 0xb4, 0x83, 0x00, 0x33, 0x83, 0x84, 0x00, + 0xb3, 0x34, 0x93, 0x00, 0x33, 0x74, 0x94, 0x00, 0x63, 0x0a, 0x04, 0x00, 0x33, 0x84, 0xc3, 0x00, 0xb3, 0x33, 0x74, + 0x00, 0x33, 0x03, 0x73, 0x00, 0x93, 0x03, 0x04, 0x00, 0x33, 0x84, 0xef, 0x00, 0x33, 0x87, 0xbe, 0x00, 0xb3, 0x35, + 0xf4, 0x01, 0xb3, 0x0f, 0xb7, 0x00, 0x23, 0x24, 0x78, 0x00, 0x23, 0x26, 0x68, 0x00, 0x63, 0x84, 0xdf, 0x01, 0xb3, + 0xb5, 0xdf, 0x01, 0x33, 0x07, 0xb4, 0x40, 0x33, 0x33, 0x87, 0x00, 0xb3, 0x85, 0x6f, 0x00, 0xb3, 0xb3, 0xf5, 0x01, + 0x33, 0x73, 0x73, 0x00, 0x63, 0x0a, 0x03, 0x00, 0x33, 0x03, 0xc7, 0x00, 0x33, 0x37, 0xe3, 0x00, 0xb3, 0x85, 0xe5, + 0x00, 0x13, 0x07, 0x03, 0x00, 0xb3, 0x02, 0x5f, 0x00, 0xb3, 0x08, 0x1e, 0x01, 0x33, 0xb3, 0xe2, 0x01, 0xb3, 0x88, + 0x68, 0x00, 0x23, 0x20, 0xe8, 0x00, 0x23, 0x22, 0xb8, 0x00, 0x63, 0x9c, 0xc8, 0x1d, 0x33, 0x87, 0x62, 0x40, 0xb3, + 0x32, 0x57, 0x00, 0xb3, 0x85, 0x58, 0x00, 0xb3, 0xb8, 0x15, 0x01, 0xb3, 0xf8, 0x12, 0x01, 0xe3, 0x88, 0x08, 0xd8, + 0x6f, 0x00, 0x80, 0x1d, 0xb3, 0xb2, 0x6f, 0x00, 0xb3, 0x82, 0x58, 0x40, 0xb3, 0xb9, 0x12, 0x01, 0xb3, 0x88, 0x3f, + 0x01, 0xb3, 0xbf, 0xf8, 0x01, 0xb3, 0xff, 0xf9, 0x01, 0xe3, 0x86, 0x0f, 0xe0, 0xb3, 0x8f, 0xc2, 0x00, 0xb3, 0xb2, + 0x5f, 0x00, 0xb3, 0x88, 0x58, 0x00, 0x93, 0x82, 0x0f, 0x00, 0xb3, 0x0f, 0xf7, 0x01, 0xb3, 0xb9, 0xef, 0x00, 0xb3, + 0x8a, 0x15, 0x01, 0xb3, 0x8a, 0x3a, 0x01, 0xe3, 0x8e, 0xba, 0xde, 0xb3, 0xb9, 0xba, 0x00, 0x33, 0x8a, 0x3f, 0x41, + 0xb3, 0x3f, 0xfa, 0x01, 0xb3, 0x89, 0xfa, 0x01, 0xb3, 0xba, 0x59, 0x01, 0xb3, 0xff, 0x5f, 0x01, 0xe3, 0x8c, 0x0f, + 0xde, 0xb3, 0x0f, 0xca, 0x00, 0x33, 0xba, 0x4f, 0x01, 0xb3, 0x89, 0x49, 0x01, 0x13, 0x8a, 0x0f, 0x00, 0xb3, 0x8e, + 0xdf, 0x01, 0xb3, 0xbf, 0xfe, 0x01, 0x33, 0x8e, 0xc9, 0x01, 0x33, 0x0e, 0xfe, 0x01, 0xe3, 0x04, 0x3e, 0xdf, 0xb3, + 0x3f, 0x3e, 0x01, 0xb3, 0x8f, 0xfe, 0x41, 0xb3, 0xba, 0xdf, 0x01, 0xb3, 0x0e, 0x5e, 0x01, 0x33, 0xbe, 0xce, 0x01, + 0x33, 0xfe, 0xca, 0x01, 0xe3, 0x02, 0x0e, 0xde, 0x33, 0x8e, 0xcf, 0x00, 0xb3, 0x3f, 0xfe, 0x01, 0xb3, 0x8e, 0xfe, + 0x01, 0x93, 0x0f, 0x0e, 0x00, 0x33, 0x09, 0x2a, 0x01, 0x33, 0x3e, 0x49, 0x01, 0x33, 0x8f, 0xe9, 0x01, 0x33, 0x0a, + 0xcf, 0x01, 0xe3, 0x0a, 0x3a, 0xdd, 0x33, 0x3e, 0x3a, 0x01, 0x33, 0x0f, 0xc9, 0x41, 0x33, 0x39, 0x2f, 0x01, 0x33, + 0x0e, 0x2a, 0x01, 0xb3, 0x39, 0x4e, 0x01, 0x33, 0x79, 0x39, 0x01, 0xe3, 0x08, 0x09, 0xdc, 0x33, 0x09, 0xcf, 0x00, + 0x33, 0x3f, 0xe9, 0x01, 0x33, 0x0e, 0xee, 0x01, 0x13, 0x0f, 0x09, 0x00, 0x33, 0x89, 0x94, 0x00, 0xb3, 0x34, 0x99, + 0x00, 0xb3, 0x09, 0x84, 0x00, 0xb3, 0x89, 0x99, 0x00, 0xe3, 0x80, 0x89, 0xdc, 0xb3, 0xb4, 0x89, 0x00, 0xb3, 0x04, + 0x99, 0x40, 0x33, 0xb9, 0x24, 0x01, 0x33, 0x84, 0x29, 0x01, 0xb3, 0x39, 0x34, 0x01, 0x33, 0x79, 0x39, 0x01, 0xe3, + 0x0e, 0x09, 0xda, 0x33, 0x89, 0xc4, 0x00, 0xb3, 0x34, 0x99, 0x00, 0x33, 0x04, 0x94, 0x00, 0x33, 0x09, 0x2f, 0x01, + 0xb3, 0x34, 0xe9, 0x01, 0x33, 0x04, 0x8e, 0x00, 0xb3, 0x09, 0x94, 0x00, 0xe3, 0x88, 0xc9, 0xdb, 0xb3, 0xb4, 0xc9, + 0x01, 0xb3, 0x04, 0x99, 0x40, 0x33, 0xb9, 0x24, 0x01, 0x33, 0x84, 0x29, 0x01, 0xb3, 0x39, 0x34, 0x01, 0x33, 0x79, + 0x39, 0x01, 0xe3, 0x16, 0x09, 0xda, 0x6f, 0xf0, 0x9f, 0xdb, 0xb3, 0xb3, 0x69, 0x00, 0xb3, 0x03, 0x79, 0x40, 0x33, + 0xb4, 0x23, 0x01, 0x33, 0x83, 0x89, 0x00, 0xb3, 0x34, 0x33, 0x01, 0x33, 0x74, 0x94, 0x00, 0xe3, 0x08, 0x04, 0xdc, + 0x33, 0x84, 0xc3, 0x00, 0xb3, 0x33, 0x74, 0x00, 0x33, 0x03, 0x73, 0x00, 0x33, 0x84, 0x8f, 0x00, 0xb3, 0x33, 0xf4, + 0x01, 0x33, 0x83, 0x6e, 0x00, 0xb3, 0x04, 0x73, 0x00, 0xe3, 0x82, 0xd4, 0xdd, 0xb3, 0xb3, 0xd4, 0x01, 0xb3, 0x03, + 0x74, 0x40, 0x33, 0xb4, 0x83, 0x00, 0x33, 0x83, 0x84, 0x00, 0xb3, 0x34, 0x93, 0x00, 0x33, 0x74, 0x94, 0x00, 0xe3, + 0x10, 0x04, 0xdc, 0x6f, 0xf0, 0xdf, 0xdc, 0x33, 0xb3, 0xc8, 0x01, 0x33, 0x87, 0x62, 0x40, 0xb3, 0x32, 0x57, 0x00, + 0xb3, 0x85, 0x58, 0x00, 0xb3, 0xb8, 0x15, 0x01, 0xb3, 0xf8, 0x12, 0x01, 0xe3, 0x8c, 0x08, 0xba, 0xb3, 0x08, 0xc7, + 0x00, 0x33, 0xb7, 0xe8, 0x00, 0xb3, 0x85, 0xe5, 0x00, 0x13, 0x87, 0x08, 0x00, 0x6f, 0xf0, 0x5f, 0xba, 0x03, 0x2e, + 0x45, 0x02, 0x03, 0x2c, 0x05, 0x00, 0x03, 0x27, 0x45, 0x00, 0x83, 0x28, 0x05, 0x02, 0x33, 0x08, 0xc7, 0x01, 0x23, + 0x20, 0x11, 0x03, 0xb3, 0x08, 0x1c, 0x01, 0xb3, 0xb5, 0x88, 0x01, 0x33, 0x08, 0xb8, 0x00, 0x63, 0x04, 0xe8, 0x00, + 0xb3, 0x35, 0xe8, 0x00, 0x83, 0x22, 0x05, 0x04, 0x83, 0x23, 0x45, 0x04, 0x23, 0x20, 0x51, 0x04, 0xb3, 0x82, 0x58, + 0x00, 0x33, 0xb3, 0x12, 0x01, 0xb3, 0x08, 0x78, 0x00, 0xb3, 0x88, 0x68, 0x00, 0x63, 0x84, 0x08, 0x01, 0x33, 0xb3, + 0x08, 0x01, 0xb3, 0x85, 0x65, 0x00, 0x33, 0xb8, 0xc5, 0x02, 0xb3, 0x8b, 0xb2, 0x40, 0x33, 0xbd, 0x5b, 0x00, 0x33, + 0x88, 0x08, 0x01, 0xb3, 0x0c, 0xa8, 0x01, 0x63, 0x84, 0x1c, 0x01, 0x33, 0xbd, 0x1c, 0x01, 0x23, 0x22, 0x71, 0x04, + 0x83, 0x2e, 0xc5, 0x02, 0x83, 0x2d, 0x85, 0x00, 0x83, 0x28, 0xc5, 0x00, 0x83, 0x22, 0x85, 0x02, 0x33, 0x88, 0xd8, + 0x01, 0x23, 0x2c, 0x51, 0x00, 0xb3, 0x82, 0x5d, 0x00, 0xb3, 0xb5, 0xb2, 0x01, 0x33, 0x08, 0xb8, 0x00, 0x63, 0x04, + 0x18, 0x01, 0xb3, 0x35, 0x18, 0x01, 0x83, 0x23, 0x85, 0x04, 0x03, 0x23, 0xc5, 0x04, 0x23, 0x2c, 0x71, 0x02, 0xb3, + 0x83, 0x72, 0x00, 0xb3, 0xb2, 0x53, 0x00, 0x23, 0x2e, 0x61, 0x02, 0x33, 0x03, 0x68, 0x00, 0x33, 0x03, 0x53, 0x00, + 0x63, 0x04, 0x03, 0x01, 0xb3, 0x32, 0x03, 0x01, 0x23, 0x22, 0xd1, 0x03, 0x23, 0x24, 0xc1, 0x03, 0xb3, 0x85, 0x55, + 0x00, 0x33, 0xbe, 0xc5, 0x02, 0x33, 0x88, 0xb3, 0x40, 0xb3, 0x32, 0x78, 0x00, 0xb3, 0x03, 0xc3, 0x01, 0xb3, 0x83, + 0x53, 0x00, 0x63, 0x84, 0x63, 0x00, 0xb3, 0xb2, 0x63, 0x00, 0x83, 0x2e, 0x45, 0x03, 0x03, 0x2e, 0x05, 0x01, 0x83, + 0x25, 0x45, 0x01, 0x03, 0x2f, 0x05, 0x03, 0x23, 0x2e, 0xd1, 0x01, 0xb3, 0x8e, 0xd5, 0x01, 0x23, 0x28, 0xe1, 0x01, + 0x33, 0x0f, 0xee, 0x01, 0x33, 0x33, 0xcf, 0x01, 0xb3, 0x8e, 0x6e, 0x00, 0x63, 0x84, 0xbe, 0x00, 0x33, 0xb3, 0xbe, + 0x00, 0x83, 0x2f, 0x05, 0x05, 0x83, 0x24, 0x45, 0x05, 0x23, 0x28, 0xf1, 0x03, 0xb3, 0x0f, 0xff, 0x01, 0x33, 0xb4, + 0xef, 0x01, 0x23, 0x2a, 0x91, 0x02, 0x33, 0x8f, 0x9e, 0x00, 0x33, 0x0f, 0x8f, 0x00, 0x63, 0x04, 0xdf, 0x01, 0x33, + 0x34, 0xdf, 0x01, 0x33, 0x03, 0x83, 0x00, 0x33, 0x34, 0xc3, 0x02, 0xb3, 0x8e, 0x6f, 0x40, 0xb3, 0xb0, 0xfe, 0x01, + 0x33, 0x04, 0x8f, 0x00, 0xb3, 0x09, 0x14, 0x00, 0x63, 0x84, 0xe9, 0x01, 0xb3, 0xb0, 0xe9, 0x01, 0x83, 0x24, 0xc5, + 0x03, 0x03, 0x2f, 0x85, 0x01, 0x03, 0x23, 0xc5, 0x01, 0x03, 0x24, 0x85, 0x03, 0x23, 0x2a, 0x91, 0x00, 0xb3, 0x04, + 0x93, 0x00, 0x23, 0x26, 0x81, 0x00, 0x33, 0x04, 0x8f, 0x00, 0xb3, 0x3f, 0xe4, 0x01, 0xb3, 0x84, 0xf4, 0x01, 0x63, + 0x84, 0x64, 0x00, 0xb3, 0xbf, 0x64, 0x00, 0x03, 0x2b, 0x85, 0x05, 0x03, 0x29, 0xc5, 0x05, 0x23, 0x26, 0x61, 0x03, + 0x33, 0x0b, 0x64, 0x01, 0xb3, 0x3a, 0x8b, 0x00, 0x13, 0x0a, 0x09, 0x00, 0x33, 0x84, 0x24, 0x01, 0x33, 0x04, 0x54, + 0x01, 0x63, 0x04, 0x94, 0x00, 0xb3, 0x3a, 0x94, 0x00, 0xb3, 0x8f, 0x5f, 0x01, 0x33, 0xb9, 0xcf, 0x02, 0xb3, 0x0a, + 0xfb, 0x41, 0xb3, 0xb4, 0x6a, 0x01, 0x33, 0x09, 0x24, 0x01, 0x33, 0x0b, 0x99, 0x00, 0x63, 0x04, 0x8b, 0x00, 0xb3, + 0x34, 0x8b, 0x00, 0xb3, 0x0f, 0xa0, 0x41, 0xb3, 0x8b, 0xab, 0x41, 0xb3, 0xbf, 0xfb, 0x01, 0x33, 0x04, 0x7c, 0x01, + 0xb3, 0x8c, 0xfc, 0x01, 0x33, 0x09, 0x97, 0x01, 0xb3, 0x3f, 0x84, 0x01, 0x33, 0x0c, 0xf9, 0x01, 0x63, 0x04, 0xec, + 0x00, 0xb3, 0x3f, 0xec, 0x00, 0xb3, 0x0f, 0xf4, 0x41, 0x33, 0xb4, 0x8f, 0x00, 0x33, 0x07, 0x8c, 0x00, 0x33, 0x39, + 0x87, 0x01, 0x33, 0x74, 0x24, 0x01, 0x63, 0x0a, 0x04, 0x00, 0x33, 0x84, 0xcf, 0x00, 0xb3, 0x3f, 0xf4, 0x01, 0x33, + 0x07, 0xf7, 0x01, 0x93, 0x0f, 0x04, 0x00, 0x33, 0x04, 0x50, 0x40, 0x33, 0x0c, 0x58, 0x40, 0x33, 0x3d, 0x8c, 0x00, + 0xb3, 0x82, 0x8d, 0x01, 0x33, 0x8d, 0xa3, 0x01, 0x33, 0xb8, 0xb2, 0x01, 0xb3, 0x83, 0xa8, 0x01, 0xb3, 0x83, 0x03, + 0x01, 0x23, 0x20, 0xf5, 0x01, 0x23, 0x22, 0xe5, 0x00, 0x63, 0x84, 0x13, 0x01, 0x33, 0xb8, 0x13, 0x01, 0x33, 0x88, + 0x02, 0x41, 0xb3, 0x38, 0x58, 0x00, 0x33, 0x87, 0x13, 0x01, 0xb3, 0x32, 0x77, 0x00, 0xb3, 0xf8, 0x58, 0x00, 0x63, + 0x8a, 0x08, 0x00, 0xb3, 0x08, 0xc8, 0x00, 0x33, 0xb8, 0x08, 0x01, 0x33, 0x07, 0x07, 0x01, 0x13, 0x88, 0x08, 0x00, + 0xb3, 0x08, 0x10, 0x40, 0xb3, 0x8d, 0x1e, 0x40, 0xb3, 0xb0, 0x1d, 0x01, 0xb3, 0x08, 0xbe, 0x01, 0xb3, 0x80, 0x19, + 0x00, 0xb3, 0xb3, 0xc8, 0x01, 0xb3, 0x82, 0x15, 0x00, 0xb3, 0x82, 0x72, 0x00, 0x23, 0x24, 0x05, 0x01, 0x23, 0x26, + 0xe5, 0x00, 0x63, 0x84, 0xb2, 0x00, 0xb3, 0xb3, 0xb2, 0x00, 0x33, 0x88, 0x78, 0x40, 0x33, 0x37, 0x18, 0x01, 0xb3, + 0x85, 0xe2, 0x00, 0xb3, 0xb8, 0x55, 0x00, 0x33, 0x77, 0x17, 0x01, 0x83, 0x2e, 0x81, 0x02, 0x63, 0x0a, 0x07, 0x00, + 0x33, 0x07, 0xc8, 0x00, 0x33, 0x38, 0x07, 0x01, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x08, 0x07, 0x00, 0xb3, 0x08, 0x90, + 0x40, 0x33, 0x87, 0x9a, 0x40, 0xb3, 0x38, 0x17, 0x01, 0xb3, 0x02, 0xef, 0x00, 0xb3, 0x08, 0x1b, 0x01, 0x33, 0xbe, + 0xe2, 0x01, 0xb3, 0x03, 0x13, 0x01, 0xb3, 0x83, 0xc3, 0x01, 0x23, 0x28, 0x05, 0x01, 0x23, 0x2a, 0xb5, 0x00, 0x63, + 0x84, 0x63, 0x00, 0x33, 0xbe, 0x63, 0x00, 0x33, 0x88, 0xc2, 0x41, 0xb3, 0x32, 0x58, 0x00, 0xb3, 0x85, 0x53, 0x00, + 0x33, 0xb3, 0x75, 0x00, 0xb3, 0xf2, 0x62, 0x00, 0x03, 0x2e, 0x41, 0x03, 0x03, 0x2f, 0x41, 0x01, 0x63, 0x8a, 0x02, + 0x00, 0xb3, 0x02, 0xc8, 0x00, 0x33, 0xb8, 0x02, 0x01, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x88, 0x02, 0x00, 0x83, 0x23, + 0x01, 0x02, 0xb3, 0x82, 0x73, 0x01, 0x33, 0x83, 0x9e, 0x01, 0xb3, 0xb3, 0x72, 0x00, 0x33, 0x03, 0x73, 0x00, 0x23, + 0x2c, 0x05, 0x01, 0x23, 0x2e, 0xb5, 0x00, 0x63, 0x04, 0xd3, 0x01, 0xb3, 0x33, 0xd3, 0x01, 0x33, 0x88, 0x72, 0x40, + 0xb3, 0x32, 0x58, 0x00, 0xb3, 0x05, 0x53, 0x00, 0x33, 0xb3, 0x65, 0x00, 0xb3, 0xf2, 0x62, 0x00, 0x63, 0x8a, 0x02, + 0x00, 0xb3, 0x02, 0xc8, 0x00, 0x33, 0xb8, 0x02, 0x01, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x88, 0x02, 0x00, 0x83, 0x2e, + 0x41, 0x02, 0x83, 0x23, 0x81, 0x01, 0xb3, 0x82, 0x83, 0x01, 0x33, 0x83, 0xae, 0x01, 0xb3, 0xb3, 0x72, 0x00, 0x33, + 0x03, 0x73, 0x00, 0x23, 0x20, 0x05, 0x03, 0x23, 0x22, 0xb5, 0x02, 0x63, 0x04, 0xd3, 0x01, 0xb3, 0x33, 0xd3, 0x01, + 0x33, 0x88, 0x72, 0x40, 0xb3, 0x32, 0x58, 0x00, 0xb3, 0x05, 0x53, 0x00, 0x33, 0xb3, 0x65, 0x00, 0xb3, 0xf2, 0x62, + 0x00, 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x02, 0xc8, 0x00, 0x33, 0xb8, 0x02, 0x01, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x88, + 0x02, 0x00, 0x83, 0x2e, 0xc1, 0x01, 0x83, 0x23, 0x01, 0x01, 0xb3, 0x82, 0xb3, 0x01, 0x33, 0x83, 0x1e, 0x00, 0xb3, + 0xb3, 0x72, 0x00, 0x33, 0x03, 0x73, 0x00, 0x23, 0x24, 0x05, 0x03, 0x23, 0x26, 0xb5, 0x02, 0x63, 0x04, 0xd3, 0x01, + 0xb3, 0x33, 0xd3, 0x01, 0x33, 0x88, 0x72, 0x40, 0xb3, 0x32, 0x58, 0x00, 0xb3, 0x05, 0x53, 0x00, 0x33, 0xb3, 0x65, + 0x00, 0xb3, 0xf2, 0x62, 0x00, 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x02, 0xc8, 0x00, 0x33, 0xb8, 0x02, 0x01, 0xb3, 0x85, + 0x05, 0x01, 0x13, 0x88, 0x02, 0x00, 0x83, 0x23, 0xc1, 0x00, 0xb3, 0x82, 0xe3, 0x00, 0x33, 0x03, 0x1f, 0x01, 0xb3, + 0xb3, 0x72, 0x00, 0x33, 0x03, 0x73, 0x00, 0x23, 0x28, 0x05, 0x03, 0x23, 0x2a, 0xb5, 0x02, 0x63, 0x04, 0xe3, 0x01, + 0xb3, 0x33, 0xe3, 0x01, 0x33, 0x88, 0x72, 0x40, 0xb3, 0x32, 0x58, 0x00, 0xb3, 0x05, 0x53, 0x00, 0x33, 0xb3, 0x65, + 0x00, 0xb3, 0xf2, 0x62, 0x00, 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x02, 0xc8, 0x00, 0x33, 0xb8, 0x02, 0x01, 0xb3, 0x85, + 0x05, 0x01, 0x13, 0x88, 0x02, 0x00, 0x83, 0x23, 0x41, 0x04, 0x03, 0x23, 0x01, 0x04, 0xb3, 0x0b, 0x73, 0x01, 0xb3, + 0x82, 0x93, 0x01, 0x33, 0xb3, 0x6b, 0x00, 0xb3, 0x82, 0x62, 0x00, 0x23, 0x2c, 0x05, 0x03, 0x23, 0x2e, 0xb5, 0x02, + 0x63, 0x84, 0x72, 0x00, 0x33, 0xb3, 0x72, 0x00, 0x33, 0x88, 0x6b, 0x40, 0x33, 0x33, 0x78, 0x01, 0xb3, 0x85, 0x62, + 0x00, 0xb3, 0xb2, 0x55, 0x00, 0xb3, 0x72, 0x53, 0x00, 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x02, 0xc8, 0x00, 0x33, 0xb8, + 0x02, 0x01, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x88, 0x02, 0x00, 0x83, 0x23, 0xc1, 0x03, 0x03, 0x23, 0x81, 0x03, 0x33, + 0x0c, 0x83, 0x01, 0xb3, 0x82, 0xa3, 0x01, 0x33, 0x33, 0x6c, 0x00, 0xb3, 0x82, 0x62, 0x00, 0x23, 0x20, 0x05, 0x05, + 0x23, 0x22, 0xb5, 0x04, 0x63, 0x84, 0x72, 0x00, 0x33, 0xb3, 0x72, 0x00, 0x33, 0x08, 0x6c, 0x40, 0x33, 0x33, 0x88, + 0x01, 0xb3, 0x85, 0x62, 0x00, 0xb3, 0xb2, 0x55, 0x00, 0xb3, 0x72, 0x53, 0x00, 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x02, + 0xc8, 0x00, 0x33, 0xb8, 0x02, 0x01, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x88, 0x02, 0x00, 0x03, 0x23, 0x01, 0x03, 0xb3, + 0x0d, 0xb3, 0x01, 0xb3, 0x02, 0x1e, 0x00, 0x33, 0xb3, 0x6d, 0x00, 0xb3, 0x82, 0x62, 0x00, 0x23, 0x24, 0x05, 0x05, + 0x23, 0x26, 0xb5, 0x04, 0x63, 0x84, 0xc2, 0x01, 0x33, 0xb3, 0xc2, 0x01, 0x33, 0x88, 0x6d, 0x40, 0x33, 0x33, 0xb8, + 0x01, 0xb3, 0x85, 0x62, 0x00, 0xb3, 0xb2, 0x55, 0x00, 0xb3, 0x72, 0x53, 0x00, 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x02, + 0xc8, 0x00, 0x33, 0xb8, 0x02, 0x01, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x88, 0x02, 0x00, 0x03, 0x23, 0xc1, 0x02, 0xb3, + 0x02, 0xe3, 0x00, 0xb3, 0x08, 0x1a, 0x01, 0x33, 0xb7, 0x62, 0x00, 0xb3, 0x88, 0xe8, 0x00, 0x23, 0x28, 0x05, 0x05, + 0x23, 0x2a, 0xb5, 0x04, 0x63, 0x90, 0x48, 0x03, 0x33, 0x87, 0xe2, 0x40, 0x33, 0x38, 0x57, 0x00, 0xb3, 0x82, 0x08, + 0x01, 0xb3, 0xb8, 0x12, 0x01, 0x33, 0x78, 0x18, 0x01, 0x63, 0x0e, 0x08, 0xa6, 0x6f, 0x00, 0x00, 0x02, 0x33, 0xb7, + 0x48, 0x01, 0x33, 0x87, 0xe2, 0x40, 0x33, 0x38, 0x57, 0x00, 0xb3, 0x82, 0x08, 0x01, 0xb3, 0xb8, 0x12, 0x01, 0x33, + 0x78, 0x18, 0x01, 0x63, 0x0e, 0x08, 0xa4, 0x33, 0x08, 0xc7, 0x00, 0x33, 0x37, 0xe8, 0x00, 0xb3, 0x82, 0xe2, 0x00, + 0x13, 0x07, 0x08, 0x00, 0x6f, 0xf0, 0x8f, 0xa4, 0x83, 0x20, 0xc1, 0x07, 0x03, 0x24, 0x81, 0x07, 0x83, 0x24, 0x41, + 0x07, 0x03, 0x29, 0x01, 0x07, 0x83, 0x29, 0xc1, 0x06, 0x03, 0x2a, 0x81, 0x06, 0x83, 0x2a, 0x41, 0x06, 0x03, 0x2b, + 0x01, 0x06, 0x83, 0x2b, 0xc1, 0x05, 0x03, 0x2c, 0x81, 0x05, 0x83, 0x2c, 0x41, 0x05, 0x03, 0x2d, 0x01, 0x05, 0x83, + 0x2d, 0xc1, 0x04, 0x13, 0x01, 0x01, 0x08, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2e, 0x11, 0x00, + 0x23, 0x2c, 0x81, 0x00, 0x23, 0x2a, 0x91, 0x00, 0x23, 0x28, 0x21, 0x01, 0x23, 0x26, 0x31, 0x01, 0x23, 0x24, 0x41, + 0x01, 0x23, 0x22, 0x51, 0x01, 0x83, 0x2a, 0x85, 0x00, 0x63, 0x86, 0x0a, 0x38, 0x13, 0x84, 0x05, 0x00, 0x03, 0x29, + 0x45, 0x00, 0x93, 0x09, 0xf0, 0xff, 0x13, 0x0a, 0x10, 0x00, 0x93, 0x9a, 0x3a, 0x00, 0xb7, 0xa4, 0x00, 0x00, 0x93, + 0x84, 0x84, 0x54, 0x6f, 0x00, 0x80, 0x03, 0x13, 0x09, 0x89, 0x00, 0xb3, 0x05, 0xd0, 0x40, 0x33, 0x05, 0xd5, 0x40, + 0xb3, 0x35, 0xb5, 0x00, 0xb3, 0x05, 0xb6, 0x00, 0x23, 0x20, 0xa4, 0x00, 0x23, 0x22, 0xb4, 0x00, 0x13, 0x05, 0x04, + 0x00, 0x93, 0x85, 0x04, 0x00, 0x97, 0x10, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xe2, 0x93, 0x8a, 0x8a, 0xff, 0x63, 0x8c, + 0x0a, 0x32, 0x83, 0x25, 0x09, 0x00, 0x03, 0x26, 0x49, 0x00, 0x03, 0x25, 0x44, 0x00, 0x83, 0x26, 0x04, 0x00, 0x33, + 0x06, 0xc5, 0x00, 0xb3, 0x85, 0xb6, 0x00, 0xb3, 0xb6, 0xd5, 0x00, 0x33, 0x06, 0xd6, 0x00, 0x63, 0x04, 0xa6, 0x00, + 0xb3, 0x36, 0xa6, 0x00, 0x33, 0x85, 0xd5, 0x40, 0xb3, 0x36, 0xb5, 0x00, 0xb3, 0x05, 0xd6, 0x00, 0x33, 0xb6, 0xc5, + 0x00, 0x33, 0xf6, 0xc6, 0x00, 0x63, 0x0a, 0x06, 0x00, 0x33, 0x06, 0x35, 0x01, 0x33, 0x35, 0xa6, 0x00, 0xb3, 0x85, + 0xa5, 0x00, 0x13, 0x05, 0x06, 0x00, 0x33, 0x86, 0xa5, 0x02, 0xb3, 0x36, 0xa5, 0x02, 0x33, 0xb7, 0xa5, 0x02, 0xb3, + 0x87, 0xb5, 0x02, 0xb3, 0x06, 0xd6, 0x00, 0x33, 0xb8, 0xc6, 0x00, 0xb3, 0x06, 0xd6, 0x00, 0x33, 0x08, 0x07, 0x01, + 0x33, 0xb6, 0xc6, 0x00, 0x33, 0x06, 0xc7, 0x00, 0x33, 0xb7, 0xb5, 0x02, 0x33, 0x06, 0xc8, 0x00, 0x33, 0x38, 0x06, + 0x01, 0x33, 0x86, 0xc7, 0x00, 0xb3, 0x37, 0xf6, 0x00, 0x33, 0x07, 0x07, 0x01, 0x33, 0x07, 0xf7, 0x00, 0xb3, 0x07, + 0xa5, 0x02, 0x33, 0xb8, 0xe7, 0x00, 0x63, 0x84, 0x06, 0x24, 0x93, 0x08, 0x10, 0x00, 0x33, 0x87, 0xe7, 0x40, 0xb3, + 0x86, 0x06, 0x41, 0x63, 0x9c, 0x08, 0x00, 0xb3, 0x07, 0x47, 0x01, 0x33, 0xb7, 0xe7, 0x00, 0xb3, 0x86, 0x36, 0x01, + 0xb3, 0x86, 0xe6, 0x00, 0x13, 0x87, 0x07, 0x00, 0x33, 0x76, 0x36, 0x01, 0xb3, 0x37, 0x36, 0x03, 0x33, 0x06, 0xc7, + 0x40, 0x33, 0x88, 0xd7, 0x00, 0xb3, 0x37, 0xe6, 0x00, 0x33, 0x07, 0xf8, 0x00, 0x63, 0x04, 0xd7, 0x00, 0xb3, 0x37, + 0xd7, 0x00, 0xb3, 0x06, 0xf0, 0x40, 0x33, 0x06, 0xf6, 0x40, 0xb3, 0x36, 0xd6, 0x00, 0xb3, 0x37, 0xa6, 0x02, 0x33, + 0x08, 0xb6, 0x02, 0xb3, 0x06, 0xd7, 0x00, 0x33, 0x87, 0xa6, 0x02, 0xb3, 0xb8, 0xa6, 0x02, 0xb3, 0x07, 0xf7, 0x00, + 0x33, 0xb7, 0xe7, 0x00, 0xb3, 0x88, 0xe8, 0x00, 0xb3, 0x32, 0xb6, 0x02, 0x33, 0x07, 0xf8, 0x00, 0xb3, 0x37, 0x07, + 0x01, 0xb3, 0x87, 0xf2, 0x00, 0x33, 0xb8, 0xb6, 0x02, 0xb3, 0x82, 0xb6, 0x02, 0xb3, 0x85, 0xf8, 0x00, 0xb3, 0xb7, + 0x15, 0x01, 0xb3, 0x85, 0xb2, 0x00, 0xb3, 0x07, 0xf8, 0x00, 0x33, 0xb8, 0x55, 0x00, 0xb3, 0x87, 0x07, 0x01, 0x33, + 0x05, 0xa6, 0x02, 0x33, 0x38, 0xf5, 0x00, 0x63, 0x08, 0x07, 0x1a, 0x93, 0x08, 0x10, 0x00, 0x33, 0x05, 0xf5, 0x40, + 0xb3, 0x07, 0x07, 0x41, 0x63, 0x9c, 0x08, 0x00, 0x33, 0x07, 0x45, 0x01, 0x33, 0x35, 0xa7, 0x00, 0xb3, 0x87, 0x37, + 0x01, 0xb3, 0x87, 0xa7, 0x00, 0x13, 0x05, 0x07, 0x00, 0xb3, 0xf5, 0x35, 0x01, 0x33, 0xb7, 0x35, 0x03, 0xb3, 0x05, + 0xb5, 0x40, 0x33, 0x08, 0xf7, 0x00, 0x33, 0xb7, 0xa5, 0x00, 0x33, 0x05, 0xe8, 0x00, 0x63, 0x04, 0xf5, 0x00, 0x33, + 0x37, 0xf5, 0x00, 0x33, 0x88, 0xc6, 0x02, 0xb3, 0x37, 0xc6, 0x02, 0xb3, 0xb8, 0xc6, 0x02, 0xb3, 0x82, 0xd6, 0x02, + 0x33, 0xb3, 0xd6, 0x02, 0xb3, 0x07, 0xf8, 0x00, 0xb3, 0xb6, 0x07, 0x01, 0xb3, 0x07, 0xf8, 0x00, 0xb3, 0x83, 0xd8, + 0x00, 0xb3, 0xb6, 0x07, 0x01, 0xb3, 0x86, 0xd8, 0x00, 0x33, 0x88, 0xd3, 0x00, 0xb3, 0x86, 0x02, 0x01, 0x33, 0x38, + 0x78, 0x00, 0xb3, 0xb8, 0x56, 0x00, 0x33, 0x08, 0x03, 0x01, 0x33, 0x08, 0x18, 0x01, 0x33, 0x06, 0xc6, 0x02, 0xb3, + 0x38, 0x06, 0x01, 0x63, 0x88, 0x07, 0x12, 0x93, 0x02, 0x10, 0x00, 0x33, 0x08, 0x06, 0x41, 0x33, 0x86, 0x17, 0x41, + 0x63, 0x9c, 0x02, 0x00, 0xb3, 0x07, 0x48, 0x01, 0x33, 0xb8, 0x07, 0x01, 0x33, 0x06, 0x36, 0x01, 0x33, 0x06, 0x06, + 0x01, 0x13, 0x88, 0x07, 0x00, 0xb3, 0xf6, 0x36, 0x01, 0xb3, 0xb8, 0x36, 0x03, 0xb3, 0x07, 0xd8, 0x40, 0xb3, 0x86, + 0xc8, 0x00, 0x33, 0xb8, 0x07, 0x01, 0xb3, 0x86, 0x06, 0x01, 0x63, 0x84, 0xc6, 0x00, 0x33, 0xb8, 0xc6, 0x00, 0x33, + 0x06, 0xe0, 0x40, 0x33, 0x87, 0xe5, 0x40, 0xb3, 0x05, 0x00, 0x41, 0xb3, 0x87, 0x07, 0x41, 0x33, 0x36, 0xc7, 0x00, + 0xb3, 0xb5, 0xb7, 0x00, 0x33, 0xb8, 0xe7, 0x02, 0x33, 0x05, 0xc5, 0x00, 0xb3, 0x85, 0xb6, 0x00, 0x33, 0x86, 0xe5, + 0x02, 0xb3, 0xb6, 0xe5, 0x02, 0xb3, 0x88, 0xa7, 0x02, 0x33, 0x08, 0x06, 0x01, 0x33, 0x36, 0xc8, 0x00, 0x33, 0x86, + 0xc6, 0x00, 0xb3, 0xb6, 0xa5, 0x02, 0xb3, 0x82, 0xa5, 0x02, 0x33, 0xb5, 0xa7, 0x02, 0xb3, 0x85, 0x08, 0x01, 0x33, + 0xb8, 0x15, 0x01, 0x33, 0x05, 0x05, 0x01, 0x33, 0x05, 0xa6, 0x00, 0x33, 0x36, 0xc5, 0x00, 0x33, 0x85, 0xa2, 0x00, + 0x33, 0x86, 0xc6, 0x00, 0xb3, 0x36, 0x55, 0x00, 0x33, 0x06, 0xd6, 0x00, 0xb3, 0x86, 0xe7, 0x02, 0x33, 0xb7, 0xc6, + 0x00, 0x63, 0x84, 0x05, 0x08, 0x93, 0x07, 0x10, 0x00, 0x33, 0x86, 0xc6, 0x40, 0xb3, 0x85, 0xe5, 0x40, 0x63, 0x9c, + 0x07, 0x00, 0xb3, 0x06, 0x46, 0x01, 0x33, 0xb6, 0xc6, 0x00, 0xb3, 0x85, 0x35, 0x01, 0xb3, 0x85, 0xc5, 0x00, 0x13, + 0x86, 0x06, 0x00, 0x33, 0x75, 0x35, 0x01, 0xb3, 0x36, 0x35, 0x03, 0x33, 0x05, 0xa6, 0x40, 0x33, 0x87, 0xb6, 0x00, + 0xb3, 0x36, 0xc5, 0x00, 0x33, 0x06, 0xd7, 0x00, 0xe3, 0x0a, 0xb6, 0xce, 0xb3, 0x36, 0xb6, 0x00, 0x6f, 0xf0, 0xdf, + 0xce, 0x93, 0x48, 0x18, 0x00, 0x33, 0x87, 0xe7, 0x40, 0xb3, 0x86, 0x06, 0x41, 0xe3, 0x80, 0x08, 0xdc, 0x6f, 0xf0, + 0x1f, 0xdd, 0x93, 0x48, 0x18, 0x00, 0x33, 0x05, 0xf5, 0x40, 0xb3, 0x07, 0x07, 0x41, 0xe3, 0x8c, 0x08, 0xe4, 0x6f, + 0xf0, 0x9f, 0xe6, 0x93, 0xc2, 0x18, 0x00, 0x33, 0x08, 0x06, 0x41, 0x33, 0x86, 0x17, 0x41, 0xe3, 0x8c, 0x02, 0xec, + 0x6f, 0xf0, 0x9f, 0xee, 0x93, 0x47, 0x17, 0x00, 0x33, 0x86, 0xc6, 0x40, 0xb3, 0x85, 0xe5, 0x40, 0xe3, 0x80, 0x07, + 0xf8, 0x6f, 0xf0, 0x1f, 0xf9, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, + 0x01, 0x01, 0x83, 0x29, 0xc1, 0x00, 0x03, 0x2a, 0x81, 0x00, 0x83, 0x2a, 0x41, 0x00, 0x13, 0x01, 0x01, 0x02, 0x67, + 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xf8, 0x23, 0x2e, 0x11, 0x06, 0x23, 0x2c, 0x81, 0x06, 0x23, 0x2a, 0x91, 0x06, + 0x23, 0x28, 0x21, 0x07, 0x23, 0x26, 0x31, 0x07, 0x23, 0x24, 0x41, 0x07, 0x23, 0x22, 0x51, 0x07, 0x23, 0x20, 0x61, + 0x07, 0x23, 0x2e, 0x71, 0x05, 0x23, 0x2c, 0x81, 0x05, 0x23, 0x2a, 0x91, 0x05, 0x23, 0x28, 0xa1, 0x05, 0x23, 0x26, + 0xb1, 0x05, 0x93, 0x86, 0x05, 0x00, 0x83, 0x25, 0x45, 0x00, 0x23, 0x24, 0xb1, 0x04, 0x03, 0x25, 0x85, 0x00, 0x23, + 0x22, 0xa1, 0x04, 0x13, 0x05, 0xf0, 0xff, 0x13, 0x87, 0x06, 0x06, 0x93, 0x87, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x01, + 0x23, 0xa8, 0xc7, 0x00, 0x23, 0xaa, 0xb7, 0x00, 0x93, 0x87, 0x07, 0x02, 0x63, 0x8a, 0xe7, 0x44, 0x83, 0xa5, 0xc7, + 0x00, 0x03, 0xa4, 0x07, 0x00, 0x03, 0xa6, 0x47, 0x00, 0x83, 0xae, 0x87, 0x00, 0xb3, 0x02, 0xb6, 0x00, 0x33, 0x08, + 0xd4, 0x01, 0xb3, 0x38, 0x88, 0x00, 0xb3, 0x82, 0x12, 0x01, 0x63, 0x84, 0xc2, 0x00, 0xb3, 0xb8, 0xc2, 0x00, 0xb3, + 0x08, 0x18, 0x41, 0x33, 0xb3, 0x08, 0x01, 0x33, 0x88, 0x62, 0x00, 0xb3, 0x32, 0x58, 0x00, 0xb3, 0x72, 0x53, 0x00, + 0x63, 0x8a, 0x02, 0x00, 0xb3, 0x82, 0xa8, 0x00, 0xb3, 0xb8, 0x12, 0x01, 0x33, 0x08, 0x18, 0x01, 0x93, 0x88, 0x02, + 0x00, 0x83, 0xaf, 0xc7, 0x01, 0x03, 0xae, 0x07, 0x01, 0x83, 0xa3, 0x47, 0x01, 0x83, 0xa4, 0x87, 0x01, 0x33, 0x8f, + 0xf3, 0x01, 0xb3, 0x02, 0x9e, 0x00, 0x33, 0xb3, 0xc2, 0x01, 0x33, 0x0f, 0x6f, 0x00, 0x63, 0x1c, 0x7f, 0x1e, 0x33, + 0x83, 0x62, 0x40, 0x33, 0x39, 0x53, 0x00, 0xb3, 0x02, 0x2f, 0x01, 0x33, 0xbf, 0xe2, 0x01, 0x33, 0x7f, 0xe9, 0x01, + 0x63, 0x1e, 0x0f, 0x1e, 0x33, 0x8f, 0x68, 0x00, 0x33, 0x39, 0x1f, 0x01, 0x33, 0x0a, 0x58, 0x00, 0x33, 0x0a, 0x2a, + 0x01, 0x63, 0x16, 0x0a, 0x21, 0xb3, 0x09, 0x2f, 0x41, 0x33, 0xbf, 0xe9, 0x01, 0x33, 0x09, 0xea, 0x01, 0x33, 0x3a, + 0x49, 0x01, 0x33, 0x7f, 0x4f, 0x01, 0x63, 0x18, 0x0f, 0x20, 0xb3, 0x8e, 0xd9, 0x01, 0x33, 0xbf, 0x3e, 0x01, 0xb3, + 0x05, 0xb9, 0x00, 0x33, 0x8a, 0xe5, 0x01, 0x63, 0x10, 0x2a, 0x23, 0xb3, 0x85, 0xee, 0x41, 0xb3, 0xbe, 0xd5, 0x01, + 0x33, 0x0f, 0xda, 0x01, 0x33, 0x3a, 0x4f, 0x01, 0xb3, 0xfe, 0x4e, 0x01, 0x63, 0x92, 0x0e, 0x22, 0xb3, 0x84, 0x99, + 0x00, 0xb3, 0xbe, 0x34, 0x01, 0xb3, 0x0f, 0xf9, 0x01, 0xb3, 0x89, 0xdf, 0x01, 0x63, 0x9a, 0x29, 0x23, 0xb3, 0x8f, + 0xd4, 0x41, 0xb3, 0xb4, 0x9f, 0x00, 0xb3, 0x8e, 0x99, 0x00, 0x33, 0xb9, 0x3e, 0x01, 0xb3, 0xf4, 0x24, 0x01, 0x63, + 0x9c, 0x04, 0x22, 0xb3, 0x04, 0x84, 0x00, 0x33, 0xb4, 0x84, 0x00, 0x33, 0x09, 0xc6, 0x00, 0x33, 0x09, 0x89, 0x00, + 0x63, 0x14, 0xc9, 0x24, 0x33, 0x84, 0x84, 0x40, 0xb3, 0x34, 0x94, 0x00, 0x33, 0x06, 0x99, 0x00, 0x33, 0x39, 0x26, + 0x01, 0xb3, 0xf4, 0x24, 0x01, 0x63, 0x96, 0x04, 0x24, 0xb3, 0x84, 0x8f, 0x00, 0x33, 0xb4, 0xf4, 0x01, 0x33, 0x86, + 0xce, 0x00, 0x33, 0x09, 0x86, 0x00, 0x63, 0x1c, 0xd9, 0x25, 0x33, 0x84, 0x84, 0x40, 0xb3, 0x34, 0x94, 0x00, 0x33, + 0x06, 0x99, 0x00, 0x33, 0x39, 0x26, 0x01, 0xb3, 0xf4, 0x24, 0x01, 0x63, 0x8a, 0x04, 0x00, 0xb3, 0x04, 0xa4, 0x00, + 0x33, 0xb4, 0x84, 0x00, 0x33, 0x06, 0x86, 0x00, 0x13, 0x84, 0x04, 0x00, 0xb3, 0x04, 0xce, 0x01, 0xb3, 0x89, 0x73, + 0x00, 0x33, 0xb9, 0xc4, 0x01, 0x33, 0x8e, 0x29, 0x01, 0x23, 0xac, 0x87, 0x00, 0x23, 0xae, 0xc7, 0x00, 0x63, 0x1a, + 0x7e, 0x22, 0xb3, 0x83, 0x24, 0x41, 0x33, 0xb4, 0x93, 0x00, 0x33, 0x06, 0x8e, 0x00, 0x33, 0x3e, 0xc6, 0x01, 0x33, + 0x7e, 0xc4, 0x01, 0x63, 0x1c, 0x0e, 0x22, 0x33, 0x8e, 0x75, 0x00, 0xb3, 0x33, 0xbe, 0x00, 0x33, 0x06, 0xcf, 0x00, + 0x33, 0x04, 0x76, 0x00, 0x63, 0x12, 0xe4, 0x25, 0xb3, 0x03, 0x7e, 0x40, 0x33, 0xbe, 0xc3, 0x01, 0x33, 0x06, 0xc4, + 0x01, 0x33, 0x34, 0x86, 0x00, 0x33, 0x7e, 0x8e, 0x00, 0x63, 0x0a, 0x0e, 0x00, 0x33, 0x8e, 0xa3, 0x00, 0xb3, 0x33, + 0x7e, 0x00, 0x33, 0x06, 0x76, 0x00, 0x93, 0x03, 0x0e, 0x00, 0xb3, 0x88, 0x15, 0x01, 0x33, 0x08, 0x0f, 0x01, 0xb3, + 0xb5, 0xb8, 0x00, 0x33, 0x08, 0xb8, 0x00, 0x23, 0xa4, 0x77, 0x00, 0x23, 0xa6, 0xc7, 0x00, 0x63, 0x04, 0xe8, 0x01, + 0xb3, 0x35, 0xe8, 0x01, 0x33, 0x86, 0xb8, 0x40, 0xb3, 0x38, 0x16, 0x01, 0xb3, 0x05, 0x18, 0x01, 0x33, 0xb8, 0x05, + 0x01, 0x33, 0xf8, 0x08, 0x01, 0x63, 0x0a, 0x08, 0x00, 0x33, 0x08, 0xa6, 0x00, 0x33, 0x36, 0xc8, 0x00, 0xb3, 0x85, + 0xc5, 0x00, 0x13, 0x06, 0x08, 0x00, 0x33, 0x83, 0x6f, 0x00, 0x33, 0x88, 0x5e, 0x00, 0xb3, 0x38, 0xf3, 0x01, 0x33, + 0x08, 0x18, 0x01, 0x23, 0xa0, 0xc7, 0x00, 0x23, 0xa2, 0xb7, 0x00, 0x63, 0x1c, 0xd8, 0x1d, 0x33, 0x06, 0x13, 0x41, + 0xb3, 0x38, 0x66, 0x00, 0xb3, 0x05, 0x18, 0x01, 0x33, 0xb8, 0x05, 0x01, 0x33, 0xf8, 0x08, 0x01, 0xe3, 0x08, 0x08, + 0xd8, 0x6f, 0x00, 0x80, 0x1d, 0x33, 0x33, 0x7f, 0x00, 0x33, 0x83, 0x62, 0x40, 0x33, 0x39, 0x53, 0x00, 0xb3, 0x02, + 0x2f, 0x01, 0x33, 0xbf, 0xe2, 0x01, 0x33, 0x7f, 0xe9, 0x01, 0xe3, 0x06, 0x0f, 0xe0, 0x33, 0x0f, 0xa3, 0x00, 0x33, + 0x33, 0x6f, 0x00, 0xb3, 0x82, 0x62, 0x00, 0x13, 0x03, 0x0f, 0x00, 0x33, 0x8f, 0xe8, 0x01, 0x33, 0x39, 0x1f, 0x01, + 0x33, 0x0a, 0x58, 0x00, 0x33, 0x0a, 0x2a, 0x01, 0xe3, 0x0e, 0x0a, 0xdf, 0x33, 0x39, 0x0a, 0x01, 0xb3, 0x09, 0x2f, + 0x41, 0x33, 0xbf, 0xe9, 0x01, 0x33, 0x09, 0xea, 0x01, 0x33, 0x3a, 0x49, 0x01, 0x33, 0x7f, 0x4f, 0x01, 0xe3, 0x0c, + 0x0f, 0xde, 0x33, 0x8f, 0xa9, 0x00, 0xb3, 0x39, 0x3f, 0x01, 0x33, 0x09, 0x39, 0x01, 0x93, 0x09, 0x0f, 0x00, 0xb3, + 0x0e, 0xdf, 0x01, 0x33, 0xbf, 0xee, 0x01, 0xb3, 0x05, 0xb9, 0x00, 0x33, 0x8a, 0xe5, 0x01, 0xe3, 0x04, 0x2a, 0xdf, + 0x33, 0x3f, 0x2a, 0x01, 0xb3, 0x85, 0xee, 0x41, 0xb3, 0xbe, 0xd5, 0x01, 0x33, 0x0f, 0xda, 0x01, 0x33, 0x3a, 0x4f, + 0x01, 0xb3, 0xfe, 0x4e, 0x01, 0xe3, 0x82, 0x0e, 0xde, 0xb3, 0x8e, 0xa5, 0x00, 0xb3, 0xb5, 0xbe, 0x00, 0x33, 0x0f, + 0xbf, 0x00, 0x93, 0x85, 0x0e, 0x00, 0xb3, 0x84, 0x99, 0x00, 0xb3, 0xbe, 0x34, 0x01, 0xb3, 0x0f, 0xf9, 0x01, 0xb3, + 0x89, 0xdf, 0x01, 0xe3, 0x8a, 0x29, 0xdd, 0xb3, 0xbe, 0x29, 0x01, 0xb3, 0x8f, 0xd4, 0x41, 0xb3, 0xb4, 0x9f, 0x00, + 0xb3, 0x8e, 0x99, 0x00, 0x33, 0xb9, 0x3e, 0x01, 0xb3, 0xf4, 0x24, 0x01, 0xe3, 0x88, 0x04, 0xdc, 0xb3, 0x84, 0xaf, + 0x00, 0xb3, 0xbf, 0xf4, 0x01, 0xb3, 0x8e, 0xfe, 0x01, 0x93, 0x8f, 0x04, 0x00, 0xb3, 0x04, 0x84, 0x00, 0x33, 0xb4, + 0x84, 0x00, 0x33, 0x09, 0xc6, 0x00, 0x33, 0x09, 0x89, 0x00, 0xe3, 0x00, 0xc9, 0xdc, 0x33, 0x34, 0xc9, 0x00, 0x33, + 0x84, 0x84, 0x40, 0xb3, 0x34, 0x94, 0x00, 0x33, 0x06, 0x99, 0x00, 0x33, 0x39, 0x26, 0x01, 0xb3, 0xf4, 0x24, 0x01, + 0xe3, 0x8e, 0x04, 0xda, 0xb3, 0x04, 0xa4, 0x00, 0x33, 0xb4, 0x84, 0x00, 0x33, 0x06, 0x86, 0x00, 0xb3, 0x84, 0x9f, + 0x00, 0x33, 0xb4, 0xf4, 0x01, 0x33, 0x86, 0xce, 0x00, 0x33, 0x09, 0x86, 0x00, 0xe3, 0x08, 0xd9, 0xdb, 0x33, 0x34, + 0xd9, 0x01, 0x33, 0x84, 0x84, 0x40, 0xb3, 0x34, 0x94, 0x00, 0x33, 0x06, 0x99, 0x00, 0x33, 0x39, 0x26, 0x01, 0xb3, + 0xf4, 0x24, 0x01, 0xe3, 0x96, 0x04, 0xda, 0x6f, 0xf0, 0x9f, 0xdb, 0x33, 0x39, 0x7e, 0x00, 0xb3, 0x83, 0x24, 0x41, + 0x33, 0xb4, 0x93, 0x00, 0x33, 0x06, 0x8e, 0x00, 0x33, 0x3e, 0xc6, 0x01, 0x33, 0x7e, 0xc4, 0x01, 0xe3, 0x08, 0x0e, + 0xdc, 0x33, 0x8e, 0xa3, 0x00, 0xb3, 0x33, 0x7e, 0x00, 0x33, 0x06, 0x76, 0x00, 0x33, 0x8e, 0xc5, 0x01, 0xb3, 0x33, + 0xbe, 0x00, 0x33, 0x06, 0xcf, 0x00, 0x33, 0x04, 0x76, 0x00, 0xe3, 0x02, 0xe4, 0xdd, 0xb3, 0x33, 0xe4, 0x01, 0xb3, + 0x03, 0x7e, 0x40, 0x33, 0xbe, 0xc3, 0x01, 0x33, 0x06, 0xc4, 0x01, 0x33, 0x34, 0x86, 0x00, 0x33, 0x7e, 0x8e, 0x00, + 0xe3, 0x10, 0x0e, 0xdc, 0x6f, 0xf0, 0xdf, 0xdc, 0xb3, 0x38, 0xd8, 0x01, 0x33, 0x06, 0x13, 0x41, 0xb3, 0x38, 0x66, + 0x00, 0xb3, 0x05, 0x18, 0x01, 0x33, 0xb8, 0x05, 0x01, 0x33, 0xf8, 0x08, 0x01, 0xe3, 0x0c, 0x08, 0xba, 0x33, 0x08, + 0xa6, 0x00, 0x33, 0x36, 0xc8, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x08, 0x00, 0x6f, 0xf0, 0x5f, 0xba, 0x03, + 0xa6, 0x46, 0x02, 0x83, 0xaf, 0x06, 0x00, 0x83, 0xae, 0x46, 0x00, 0x03, 0xa7, 0x06, 0x02, 0x23, 0x22, 0xc1, 0x02, + 0x33, 0x86, 0xce, 0x00, 0x23, 0x20, 0xe1, 0x02, 0x33, 0x87, 0xef, 0x00, 0xb3, 0x35, 0xf7, 0x01, 0x33, 0x06, 0xb6, + 0x00, 0x63, 0x04, 0xd6, 0x01, 0xb3, 0x35, 0xd6, 0x01, 0x83, 0xa7, 0x06, 0x04, 0x83, 0xa8, 0x46, 0x04, 0x23, 0x2e, + 0xf1, 0x02, 0xb3, 0x07, 0xf7, 0x00, 0x33, 0xb8, 0xe7, 0x00, 0x23, 0x20, 0x11, 0x05, 0x33, 0x07, 0x16, 0x01, 0x33, + 0x07, 0x07, 0x01, 0x63, 0x04, 0xc7, 0x00, 0x33, 0x38, 0xc7, 0x00, 0xb3, 0x85, 0x05, 0x01, 0x13, 0x03, 0xf0, 0xff, + 0x33, 0xb6, 0x65, 0x02, 0x33, 0x84, 0xb7, 0x40, 0xb3, 0x33, 0xf4, 0x00, 0x33, 0x06, 0xc7, 0x00, 0x33, 0x0f, 0x76, + 0x00, 0x63, 0x04, 0xef, 0x00, 0xb3, 0x33, 0xef, 0x00, 0x83, 0xa8, 0xc6, 0x02, 0x83, 0xaa, 0x86, 0x00, 0x03, 0xad, + 0xc6, 0x00, 0x03, 0xa7, 0x86, 0x02, 0x33, 0x06, 0x1d, 0x01, 0x23, 0x2a, 0xe1, 0x00, 0x33, 0x87, 0xea, 0x00, 0xb3, + 0x35, 0x57, 0x01, 0x33, 0x06, 0xb6, 0x00, 0x63, 0x04, 0xa6, 0x01, 0xb3, 0x35, 0xa6, 0x01, 0x03, 0xa8, 0x86, 0x04, + 0x83, 0xa7, 0xc6, 0x04, 0x23, 0x2a, 0x01, 0x03, 0x33, 0x08, 0x07, 0x01, 0x33, 0x37, 0xe8, 0x00, 0x23, 0x2c, 0xf1, + 0x02, 0xb3, 0x07, 0xf6, 0x00, 0xb3, 0x87, 0xe7, 0x00, 0x63, 0x84, 0xc7, 0x00, 0x33, 0xb7, 0xc7, 0x00, 0x23, 0x2e, + 0x11, 0x01, 0xb3, 0x85, 0xe5, 0x00, 0x33, 0xb6, 0x65, 0x02, 0x33, 0x07, 0xb8, 0x40, 0x33, 0x3c, 0x07, 0x01, 0x33, + 0x86, 0xc7, 0x00, 0x33, 0x08, 0x86, 0x01, 0x63, 0x04, 0xf8, 0x00, 0x33, 0x3c, 0xf8, 0x00, 0x83, 0xa8, 0x46, 0x03, + 0x83, 0xa5, 0x06, 0x01, 0x83, 0xa7, 0x46, 0x01, 0x83, 0xa2, 0x06, 0x03, 0x23, 0x2c, 0x11, 0x01, 0xb3, 0x88, 0x17, + 0x01, 0x23, 0x28, 0x51, 0x00, 0xb3, 0x82, 0x55, 0x00, 0x33, 0xb6, 0xb2, 0x00, 0xb3, 0x88, 0xc8, 0x00, 0x63, 0x84, + 0xf8, 0x00, 0x33, 0xb6, 0xf8, 0x00, 0x03, 0xae, 0x06, 0x05, 0x83, 0xa4, 0x46, 0x05, 0x23, 0x26, 0xc1, 0x03, 0x33, + 0x8e, 0xc2, 0x01, 0xb3, 0x32, 0x5e, 0x00, 0x23, 0x28, 0x91, 0x02, 0xb3, 0x84, 0x98, 0x00, 0xb3, 0x84, 0x54, 0x00, + 0x63, 0x84, 0x14, 0x01, 0xb3, 0xb2, 0x14, 0x01, 0x33, 0x06, 0x56, 0x00, 0xb3, 0x38, 0x66, 0x02, 0xb3, 0x02, 0xce, + 0x40, 0x33, 0xbe, 0xc2, 0x01, 0xb3, 0x88, 0x14, 0x01, 0x33, 0x89, 0xc8, 0x01, 0x63, 0x04, 0x99, 0x00, 0x33, 0x3e, + 0x99, 0x00, 0x83, 0xab, 0xc6, 0x03, 0x83, 0xa8, 0x86, 0x01, 0x03, 0xa6, 0xc6, 0x01, 0x03, 0xab, 0x86, 0x03, 0xb3, + 0x04, 0x74, 0x40, 0x33, 0x04, 0x76, 0x01, 0x23, 0x26, 0x61, 0x01, 0x33, 0x8b, 0x68, 0x01, 0xb3, 0x3d, 0x1b, 0x01, + 0x33, 0x04, 0xb4, 0x01, 0xb3, 0x03, 0x70, 0x40, 0x63, 0x04, 0xc4, 0x00, 0xb3, 0x3d, 0xc4, 0x00, 0x83, 0xac, 0x86, + 0x05, 0x83, 0xa0, 0xc6, 0x05, 0x23, 0x24, 0x91, 0x03, 0xb3, 0x0c, 0x9b, 0x01, 0xb3, 0xb9, 0x6c, 0x01, 0x13, 0x8a, + 0x00, 0x00, 0x33, 0x0b, 0x14, 0x00, 0x33, 0x0b, 0x3b, 0x01, 0xb3, 0xb0, 0x74, 0x00, 0x63, 0x04, 0x8b, 0x00, 0xb3, + 0x39, 0x8b, 0x00, 0xb3, 0x03, 0x80, 0x41, 0x33, 0x04, 0x87, 0x41, 0xb3, 0x89, 0x3d, 0x01, 0x33, 0xbc, 0x69, 0x02, + 0x33, 0x87, 0x3c, 0x41, 0x33, 0x33, 0x97, 0x01, 0x33, 0x0c, 0x8b, 0x01, 0xb3, 0x0d, 0x6c, 0x00, 0xb3, 0x00, 0x1f, + 0x00, 0x63, 0x84, 0x6d, 0x01, 0x33, 0xb3, 0x6d, 0x01, 0x33, 0x8b, 0x1e, 0x00, 0x33, 0x8f, 0x9f, 0x00, 0xb3, 0x39, + 0xff, 0x01, 0x33, 0x0b, 0x3b, 0x01, 0x33, 0x3c, 0x74, 0x00, 0x63, 0x04, 0xdb, 0x01, 0xb3, 0x39, 0xdb, 0x01, 0xb3, + 0x0f, 0xc0, 0x41, 0xb3, 0x8e, 0xc2, 0x41, 0xb3, 0x03, 0x3f, 0x41, 0x33, 0xbe, 0xe3, 0x01, 0xb3, 0x02, 0xcb, 0x01, + 0x33, 0xbf, 0x62, 0x01, 0x33, 0x7e, 0xee, 0x01, 0x33, 0x0c, 0x88, 0x01, 0x63, 0x0a, 0x0e, 0x00, 0x33, 0x88, 0xa3, + 0x00, 0xb3, 0x33, 0x78, 0x00, 0xb3, 0x82, 0x72, 0x00, 0x93, 0x03, 0x08, 0x00, 0x23, 0xa0, 0x76, 0x00, 0x23, 0xa2, + 0x56, 0x00, 0x33, 0x8e, 0x8a, 0x00, 0xb3, 0x32, 0x5e, 0x01, 0x33, 0x0f, 0x8d, 0x01, 0x33, 0x0f, 0x5f, 0x00, 0xb3, + 0xb3, 0xfe, 0x01, 0x63, 0x04, 0xaf, 0x01, 0xb3, 0x32, 0xaf, 0x01, 0x33, 0x08, 0x60, 0x40, 0xb3, 0x0f, 0x67, 0x40, + 0xb3, 0x02, 0x5e, 0x40, 0x33, 0xb3, 0xc2, 0x01, 0x33, 0x07, 0x6f, 0x00, 0x33, 0x3e, 0xe7, 0x01, 0x33, 0x73, 0xc3, + 0x01, 0x33, 0x0d, 0x79, 0x00, 0x63, 0x0a, 0x03, 0x00, 0x33, 0x83, 0xa2, 0x00, 0xb3, 0x32, 0x53, 0x00, 0x33, 0x07, + 0x57, 0x00, 0x93, 0x02, 0x03, 0x00, 0x23, 0xa4, 0x56, 0x00, 0x23, 0xa6, 0xe6, 0x00, 0xb3, 0x82, 0xd5, 0x01, 0xb3, + 0xb5, 0xb2, 0x00, 0x33, 0x83, 0xa7, 0x01, 0x33, 0x03, 0xb3, 0x00, 0x33, 0xb8, 0x0f, 0x01, 0x63, 0x04, 0xf3, 0x00, + 0xb3, 0x35, 0xf3, 0x00, 0x33, 0x87, 0xb2, 0x40, 0xb3, 0x37, 0x57, 0x00, 0xb3, 0x05, 0xf3, 0x00, 0xb3, 0xb2, 0x65, + 0x00, 0xb3, 0xf7, 0x57, 0x00, 0xb3, 0x8d, 0x0d, 0x01, 0x63, 0x8a, 0x07, 0x00, 0xb3, 0x07, 0xa7, 0x00, 0x33, 0xb7, + 0xe7, 0x00, 0xb3, 0x85, 0xe5, 0x00, 0x13, 0x87, 0x07, 0x00, 0xb3, 0x87, 0xf8, 0x01, 0x33, 0x08, 0xb6, 0x01, 0xb3, + 0xb8, 0x17, 0x01, 0x33, 0x08, 0x18, 0x01, 0x23, 0xa8, 0xe6, 0x00, 0x23, 0xaa, 0xb6, 0x00, 0x63, 0x04, 0xc8, 0x00, + 0xb3, 0x38, 0xc8, 0x00, 0x33, 0x86, 0x17, 0x41, 0x33, 0x37, 0xf6, 0x00, 0xb3, 0x05, 0xe8, 0x00, 0xb3, 0xb7, 0x05, + 0x01, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x07, 0xa6, 0x00, 0x33, 0x36, 0xc7, 0x00, 0xb3, 0x85, + 0xc5, 0x00, 0x13, 0x06, 0x07, 0x00, 0x83, 0x28, 0x41, 0x02, 0x03, 0x28, 0x01, 0x02, 0x33, 0x07, 0x98, 0x00, 0xb3, + 0x87, 0x18, 0x00, 0x33, 0x38, 0x07, 0x01, 0xb3, 0x87, 0x07, 0x01, 0x23, 0xac, 0xc6, 0x00, 0x23, 0xae, 0xb6, 0x00, + 0x63, 0x84, 0x17, 0x01, 0x33, 0xb8, 0x17, 0x01, 0x33, 0x06, 0x07, 0x41, 0x33, 0x37, 0xe6, 0x00, 0xb3, 0x85, 0xe7, + 0x00, 0xb3, 0xb7, 0xf5, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x07, 0xa6, 0x00, 0x33, 0x36, + 0xc7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x07, 0x00, 0x83, 0x28, 0xc1, 0x01, 0x03, 0x28, 0x41, 0x01, 0x33, + 0x07, 0x88, 0x00, 0xb3, 0x87, 0x88, 0x01, 0x33, 0x38, 0x07, 0x01, 0xb3, 0x87, 0x07, 0x01, 0x23, 0xa0, 0xc6, 0x02, + 0x23, 0xa2, 0xb6, 0x02, 0x63, 0x84, 0x17, 0x01, 0x33, 0xb8, 0x17, 0x01, 0x33, 0x06, 0x07, 0x41, 0x33, 0x37, 0xe6, + 0x00, 0xb3, 0x85, 0xe7, 0x00, 0xb3, 0xb7, 0xf5, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x07, + 0xa6, 0x00, 0x33, 0x36, 0xc7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x07, 0x00, 0x83, 0x28, 0x81, 0x01, 0x03, + 0x28, 0x01, 0x01, 0x33, 0x07, 0xd8, 0x01, 0xb3, 0x87, 0xa8, 0x01, 0x33, 0x38, 0x07, 0x01, 0xb3, 0x87, 0x07, 0x01, + 0x23, 0xa4, 0xc6, 0x02, 0x23, 0xa6, 0xb6, 0x02, 0x63, 0x84, 0x17, 0x01, 0x33, 0xb8, 0x17, 0x01, 0x33, 0x06, 0x07, + 0x41, 0x33, 0x37, 0xe6, 0x00, 0xb3, 0x85, 0xe7, 0x00, 0xb3, 0xb7, 0xf5, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, + 0x07, 0x00, 0x33, 0x07, 0xa6, 0x00, 0x33, 0x36, 0xc7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x07, 0x00, 0x03, + 0x28, 0xc1, 0x00, 0x33, 0x07, 0xf8, 0x01, 0xb3, 0x87, 0xbb, 0x01, 0x33, 0x38, 0x07, 0x01, 0xb3, 0x87, 0x07, 0x01, + 0x23, 0xa8, 0xc6, 0x02, 0x23, 0xaa, 0xb6, 0x02, 0x63, 0x84, 0x77, 0x01, 0x33, 0xb8, 0x77, 0x01, 0x33, 0x06, 0x07, + 0x41, 0x33, 0x37, 0xe6, 0x00, 0xb3, 0x85, 0xe7, 0x00, 0xb3, 0xb7, 0xf5, 0x00, 0x33, 0x77, 0xf7, 0x00, 0x63, 0x0a, + 0x07, 0x00, 0x33, 0x07, 0xa6, 0x00, 0x33, 0x36, 0xc7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x07, 0x00, 0x03, + 0x28, 0x01, 0x04, 0x83, 0x27, 0xc1, 0x03, 0xb3, 0x84, 0x97, 0x00, 0x33, 0x07, 0x18, 0x00, 0xb3, 0xb7, 0xf4, 0x00, + 0x33, 0x07, 0xf7, 0x00, 0x23, 0xac, 0xc6, 0x02, 0x23, 0xae, 0xb6, 0x02, 0x63, 0x04, 0x07, 0x01, 0xb3, 0x37, 0x07, + 0x01, 0x33, 0x86, 0xf4, 0x40, 0xb3, 0x37, 0x96, 0x00, 0xb3, 0x05, 0xf7, 0x00, 0x33, 0xb7, 0xe5, 0x00, 0x33, 0xf7, + 0xe7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x07, 0xa6, 0x00, 0x33, 0x36, 0xc7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, + 0x06, 0x07, 0x00, 0x03, 0x28, 0x81, 0x03, 0x83, 0x28, 0x01, 0x03, 0x83, 0x27, 0x41, 0x03, 0x33, 0x84, 0x87, 0x00, + 0x33, 0x07, 0x88, 0x01, 0xb3, 0x37, 0xf4, 0x00, 0x33, 0x07, 0xf7, 0x00, 0x23, 0xa0, 0xc6, 0x04, 0x23, 0xa2, 0xb6, + 0x04, 0x63, 0x04, 0x07, 0x01, 0xb3, 0x37, 0x07, 0x01, 0x33, 0x06, 0xf4, 0x40, 0xb3, 0x37, 0x86, 0x00, 0xb3, 0x05, + 0xf7, 0x00, 0x33, 0xb7, 0xe5, 0x00, 0x33, 0xf7, 0xe7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x07, 0xa6, 0x00, 0x33, + 0x36, 0xc7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x07, 0x00, 0x83, 0x27, 0xc1, 0x02, 0xb3, 0x8e, 0xd7, 0x01, + 0x33, 0x87, 0xa8, 0x01, 0xb3, 0xb7, 0xfe, 0x00, 0x33, 0x07, 0xf7, 0x00, 0x23, 0xa4, 0xc6, 0x04, 0x23, 0xa6, 0xb6, + 0x04, 0x63, 0x04, 0x17, 0x01, 0xb3, 0x37, 0x17, 0x01, 0x33, 0x86, 0xfe, 0x40, 0xb3, 0x37, 0xd6, 0x01, 0xb3, 0x05, + 0xf7, 0x00, 0x33, 0xb7, 0xe5, 0x00, 0x33, 0xf7, 0xe7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x07, 0xa6, 0x00, 0x33, + 0x36, 0xc7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x07, 0x00, 0x83, 0x27, 0x81, 0x02, 0xb3, 0x8f, 0xf7, 0x01, + 0x33, 0x07, 0xba, 0x01, 0xb3, 0xb7, 0xff, 0x00, 0x33, 0x07, 0xf7, 0x00, 0x23, 0xa8, 0xc6, 0x04, 0x23, 0xaa, 0xb6, + 0x04, 0x63, 0x04, 0x47, 0x01, 0xb3, 0x37, 0x47, 0x01, 0x33, 0x86, 0xff, 0x40, 0xb3, 0x37, 0xf6, 0x01, 0xb3, 0x05, + 0xf7, 0x00, 0x33, 0xb7, 0xe5, 0x00, 0x33, 0xf7, 0xe7, 0x00, 0x63, 0x0a, 0x07, 0x00, 0x33, 0x05, 0xa6, 0x00, 0x33, + 0x36, 0xc5, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x06, 0x05, 0x00, 0x23, 0xac, 0xc6, 0x04, 0x23, 0xae, 0xb6, 0x04, + 0x13, 0x85, 0x06, 0x00, 0x83, 0x25, 0x81, 0x04, 0x03, 0x26, 0x41, 0x04, 0x83, 0x20, 0xc1, 0x07, 0x03, 0x24, 0x81, + 0x07, 0x83, 0x24, 0x41, 0x07, 0x03, 0x29, 0x01, 0x07, 0x83, 0x29, 0xc1, 0x06, 0x03, 0x2a, 0x81, 0x06, 0x83, 0x2a, + 0x41, 0x06, 0x03, 0x2b, 0x01, 0x06, 0x83, 0x2b, 0xc1, 0x05, 0x03, 0x2c, 0x81, 0x05, 0x83, 0x2c, 0x41, 0x05, 0x03, + 0x2d, 0x01, 0x05, 0x83, 0x2d, 0xc1, 0x04, 0x13, 0x01, 0x01, 0x08, 0x17, 0xe3, 0xff, 0xff, 0x67, 0x00, 0xc3, 0x31, + 0x03, 0x26, 0xc5, 0x00, 0x83, 0x26, 0x05, 0x00, 0x83, 0x27, 0x45, 0x00, 0x03, 0x27, 0x85, 0x00, 0x33, 0x88, 0xc7, + 0x00, 0x33, 0x87, 0xe6, 0x00, 0x33, 0x36, 0xd7, 0x00, 0xb3, 0x06, 0xc8, 0x00, 0x63, 0x84, 0xf6, 0x00, 0x33, 0xb6, + 0xf6, 0x00, 0x83, 0x28, 0x05, 0x01, 0x83, 0x27, 0x45, 0x01, 0xb3, 0x08, 0x17, 0x01, 0x33, 0xb8, 0xe8, 0x00, 0x33, + 0x87, 0xf6, 0x00, 0x33, 0x07, 0x07, 0x01, 0x63, 0x04, 0xd7, 0x00, 0x33, 0x38, 0xd7, 0x00, 0x83, 0x27, 0x85, 0x01, + 0x83, 0x26, 0xc5, 0x01, 0x33, 0x08, 0x06, 0x01, 0xb3, 0x87, 0xf8, 0x00, 0xb3, 0xb2, 0x17, 0x01, 0xb3, 0x86, 0x56, + 0x00, 0xb3, 0x06, 0xd7, 0x00, 0xb3, 0x38, 0xc8, 0x00, 0x63, 0x84, 0xe6, 0x00, 0xb3, 0xb2, 0xe6, 0x00, 0x03, 0x26, + 0x05, 0x02, 0x33, 0x07, 0x58, 0x00, 0x83, 0x22, 0x45, 0x02, 0x33, 0x33, 0x07, 0x01, 0x33, 0x88, 0xc7, 0x00, 0xb3, + 0x37, 0xf8, 0x00, 0x33, 0x86, 0xf2, 0x00, 0x33, 0x86, 0xc6, 0x00, 0xb3, 0x82, 0x68, 0x00, 0x63, 0x04, 0xd6, 0x00, + 0xb3, 0x37, 0xd6, 0x00, 0x83, 0x28, 0x85, 0x02, 0xb3, 0x07, 0xf7, 0x00, 0x83, 0x26, 0xc5, 0x02, 0x33, 0xb3, 0xe7, + 0x00, 0xb3, 0x08, 0x18, 0x01, 0x33, 0xb7, 0x08, 0x01, 0xb3, 0x86, 0xe6, 0x00, 0xb3, 0x06, 0xd6, 0x00, 0xb3, 0x82, + 0x62, 0x00, 0x63, 0x84, 0xc6, 0x00, 0x33, 0xb7, 0xc6, 0x00, 0x03, 0x28, 0x05, 0x03, 0x33, 0x87, 0xe7, 0x00, 0x03, + 0x26, 0x45, 0x03, 0x33, 0x33, 0xf7, 0x00, 0x33, 0x88, 0x08, 0x01, 0xb3, 0x37, 0x18, 0x01, 0x33, 0x06, 0xf6, 0x00, + 0x33, 0x86, 0xc6, 0x00, 0xb3, 0x82, 0x62, 0x00, 0x63, 0x04, 0xd6, 0x00, 0xb3, 0x37, 0xd6, 0x00, 0x83, 0x28, 0x85, + 0x03, 0xb3, 0x07, 0xf7, 0x00, 0x83, 0x26, 0xc5, 0x03, 0x33, 0xb3, 0xe7, 0x00, 0xb3, 0x08, 0x18, 0x01, 0x33, 0xb7, + 0x08, 0x01, 0xb3, 0x86, 0xe6, 0x00, 0xb3, 0x06, 0xd6, 0x00, 0xb3, 0x82, 0x62, 0x00, 0x63, 0x84, 0xc6, 0x00, 0x33, + 0xb7, 0xc6, 0x00, 0x03, 0x26, 0x05, 0x04, 0x33, 0x87, 0xe7, 0x00, 0x03, 0x23, 0x45, 0x04, 0xb3, 0x33, 0xf7, 0x00, + 0xb3, 0x87, 0xc8, 0x00, 0x33, 0xb8, 0x17, 0x01, 0x33, 0x06, 0x03, 0x01, 0x33, 0x86, 0xc6, 0x00, 0xb3, 0x82, 0x72, + 0x00, 0x63, 0x04, 0xd6, 0x00, 0x33, 0x38, 0xd6, 0x00, 0x83, 0x28, 0x85, 0x04, 0xb3, 0x06, 0x07, 0x01, 0x03, 0x23, + 0xc5, 0x04, 0xb3, 0xb3, 0xe6, 0x00, 0x33, 0x88, 0x17, 0x01, 0xb3, 0x38, 0xf8, 0x00, 0x33, 0x07, 0x13, 0x01, 0x33, + 0x07, 0xe6, 0x00, 0xb3, 0x82, 0x72, 0x00, 0x63, 0x04, 0xc7, 0x00, 0xb3, 0x38, 0xc7, 0x00, 0x03, 0x26, 0x05, 0x05, + 0xb3, 0x88, 0x16, 0x01, 0x03, 0x23, 0x45, 0x05, 0xb3, 0xb3, 0xd8, 0x00, 0x33, 0x06, 0xc8, 0x00, 0xb3, 0x37, 0x06, + 0x01, 0xb3, 0x06, 0xf3, 0x00, 0xb3, 0x06, 0xd7, 0x00, 0xb3, 0x82, 0x72, 0x00, 0x63, 0x84, 0xe6, 0x00, 0xb3, 0xb7, + 0xe6, 0x00, 0xb3, 0x87, 0xf8, 0x00, 0x03, 0x28, 0x85, 0x05, 0xb3, 0xb8, 0x17, 0x01, 0x03, 0x27, 0xc5, 0x05, 0xb3, + 0x88, 0x12, 0x01, 0x33, 0x08, 0x06, 0x01, 0xb3, 0x32, 0xc8, 0x00, 0x33, 0x07, 0x57, 0x00, 0x33, 0x87, 0xe6, 0x00, + 0x13, 0x06, 0xf0, 0xff, 0x63, 0x04, 0xd7, 0x00, 0xb3, 0x32, 0xd7, 0x00, 0xb3, 0x82, 0x57, 0x00, 0xb3, 0xb6, 0xf2, + 0x00, 0xb3, 0x88, 0xd8, 0x00, 0xb3, 0xb7, 0xc2, 0x02, 0xb3, 0x06, 0x58, 0x40, 0x33, 0xb8, 0x06, 0x01, 0xb3, 0x87, + 0x17, 0x41, 0xb3, 0x08, 0x07, 0x01, 0xb3, 0x87, 0xf8, 0x00, 0x63, 0x84, 0xe7, 0x00, 0x33, 0xb8, 0xe7, 0x00, 0x13, + 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00, 0x23, 0x24, 0x91, 0x00, 0xb3, 0x08, 0x00, 0x41, 0xb3, 0x86, 0x06, 0x41, + 0x13, 0x87, 0x05, 0x06, 0x33, 0xb8, 0x16, 0x01, 0xb3, 0x87, 0x07, 0x01, 0x13, 0x08, 0xf0, 0xff, 0x6f, 0x00, 0x80, + 0x01, 0x23, 0x20, 0x55, 0x00, 0x23, 0x22, 0x15, 0x01, 0x93, 0x85, 0x85, 0x00, 0x13, 0x05, 0x85, 0x00, 0x63, 0x88, + 0xe5, 0x12, 0x83, 0xa3, 0x05, 0x00, 0x03, 0x2e, 0x05, 0x00, 0x83, 0x28, 0x45, 0x00, 0x83, 0xa2, 0x45, 0x00, 0x33, + 0x33, 0x7e, 0x02, 0xb3, 0x8e, 0x78, 0x02, 0x33, 0xbf, 0x78, 0x02, 0xb3, 0x0f, 0x5e, 0x02, 0x33, 0x84, 0x58, 0x02, + 0xb3, 0xb4, 0x58, 0x02, 0xb3, 0x38, 0x5e, 0x02, 0xb3, 0x82, 0x6e, 0x00, 0x33, 0xb3, 0xd2, 0x01, 0xb3, 0x82, 0x5f, + 0x00, 0x33, 0x03, 0x6f, 0x00, 0xb3, 0xbe, 0xf2, 0x01, 0xb3, 0x88, 0xd8, 0x01, 0xb3, 0x0e, 0x13, 0x01, 0xb3, 0x08, + 0xd4, 0x01, 0x33, 0xb3, 0x6e, 0x00, 0xb3, 0xbe, 0x88, 0x00, 0x33, 0x83, 0x64, 0x00, 0x33, 0x03, 0xd3, 0x01, 0xb3, + 0x03, 0x7e, 0x02, 0x33, 0xbe, 0x63, 0x00, 0x63, 0x8c, 0x02, 0x00, 0x93, 0x0e, 0x10, 0x00, 0x33, 0x83, 0x63, 0x40, + 0xb3, 0x82, 0xc2, 0x41, 0x63, 0x8c, 0x0e, 0x00, 0x6f, 0x00, 0x40, 0x02, 0x93, 0x4e, 0x1e, 0x00, 0x33, 0x83, 0x63, + 0x40, 0xb3, 0x82, 0xc2, 0x41, 0x63, 0x9a, 0x0e, 0x00, 0x13, 0x03, 0x13, 0x00, 0x93, 0x33, 0x13, 0x00, 0xb3, 0x82, + 0x72, 0x00, 0x93, 0x82, 0xf2, 0xff, 0xb3, 0xf8, 0xc8, 0x00, 0x33, 0xbe, 0x08, 0x03, 0xb3, 0x03, 0x13, 0x41, 0xb3, + 0x08, 0x5e, 0x00, 0x33, 0xb3, 0x63, 0x00, 0xb3, 0x88, 0x68, 0x00, 0x63, 0x84, 0x58, 0x00, 0x33, 0xb3, 0x58, 0x00, + 0xb3, 0x02, 0x60, 0x40, 0xb3, 0x83, 0x63, 0x40, 0xb3, 0xb2, 0x53, 0x00, 0x33, 0x83, 0xd3, 0x00, 0xb3, 0x82, 0x58, + 0x00, 0xb3, 0x38, 0x73, 0x00, 0xb3, 0x83, 0xf2, 0x00, 0xb3, 0x83, 0x13, 0x01, 0x63, 0x90, 0x53, 0x02, 0xb3, 0x02, + 0x13, 0x41, 0x33, 0xb3, 0x62, 0x00, 0xb3, 0x88, 0x63, 0x00, 0xb3, 0xb3, 0x78, 0x00, 0x33, 0x73, 0x73, 0x00, 0xe3, + 0x0c, 0x03, 0xee, 0x6f, 0x00, 0x00, 0x02, 0xb3, 0xb8, 0x53, 0x00, 0xb3, 0x02, 0x13, 0x41, 0x33, 0xb3, 0x62, 0x00, + 0xb3, 0x88, 0x63, 0x00, 0xb3, 0xb3, 0x78, 0x00, 0x33, 0x73, 0x73, 0x00, 0xe3, 0x0c, 0x03, 0xec, 0x33, 0x83, 0xc2, + 0x00, 0xb3, 0x32, 0x53, 0x00, 0xb3, 0x88, 0x58, 0x00, 0x93, 0x02, 0x03, 0x00, 0x6f, 0xf0, 0x5f, 0xec, 0x03, 0x24, + 0xc1, 0x00, 0x83, 0x24, 0x81, 0x00, 0x13, 0x01, 0x01, 0x01, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, + 0x26, 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x23, 0x22, 0x91, 0x00, 0x23, 0x20, 0x21, 0x01, 0x13, 0x04, 0x05, 0x00, + 0x63, 0x48, 0x06, 0x06, 0x93, 0x04, 0x06, 0x00, 0x13, 0x89, 0x05, 0x00, 0x03, 0xa5, 0x46, 0x00, 0x63, 0x0a, 0x05, + 0x02, 0x83, 0xa5, 0x86, 0x00, 0x63, 0x86, 0x05, 0x02, 0x03, 0xa5, 0x06, 0x00, 0x13, 0x06, 0x09, 0x00, 0x93, 0x86, + 0x04, 0x00, 0x97, 0xa0, 0xff, 0xff, 0xe7, 0x80, 0x80, 0x0a, 0x63, 0x0a, 0x05, 0x02, 0x93, 0x05, 0x00, 0x00, 0x23, + 0x22, 0xa4, 0x00, 0x23, 0x24, 0x94, 0x00, 0x6f, 0x00, 0x80, 0x03, 0x63, 0x88, 0x04, 0x04, 0x37, 0xb5, 0x02, 0x00, + 0x03, 0x40, 0x55, 0x91, 0x13, 0x85, 0x04, 0x00, 0x93, 0x05, 0x09, 0x00, 0x97, 0xa0, 0xff, 0xff, 0xe7, 0x80, 0xc0, + 0x02, 0xe3, 0x1a, 0x05, 0xfc, 0x23, 0x22, 0x24, 0x01, 0x23, 0x24, 0x94, 0x00, 0x6f, 0x00, 0x80, 0x00, 0x23, 0x22, + 0x04, 0x00, 0x93, 0x05, 0x10, 0x00, 0x23, 0x20, 0xb4, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x03, 0x24, 0x81, 0x00, 0x83, + 0x24, 0x41, 0x00, 0x03, 0x29, 0x01, 0x00, 0x13, 0x01, 0x01, 0x01, 0x67, 0x80, 0x00, 0x00, 0x13, 0x05, 0x09, 0x00, + 0xe3, 0x1e, 0x09, 0xf8, 0x6f, 0xf0, 0x9f, 0xfc, 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, 0x11, 0x02, 0x23, 0x24, 0x81, + 0x02, 0x23, 0x22, 0x91, 0x02, 0x23, 0x20, 0x21, 0x03, 0x93, 0x04, 0x05, 0x00, 0x83, 0x26, 0x05, 0x00, 0x13, 0x99, + 0x16, 0x00, 0x13, 0x05, 0x40, 0x00, 0x13, 0x84, 0x05, 0x00, 0x63, 0x64, 0x25, 0x01, 0x13, 0x09, 0x40, 0x00, 0x93, + 0xd5, 0xc6, 0x01, 0x13, 0x05, 0x00, 0x00, 0x63, 0x9c, 0x05, 0x06, 0x13, 0x16, 0x39, 0x00, 0xb7, 0x05, 0x00, 0x80, + 0x13, 0x87, 0x85, 0xff, 0x63, 0x6e, 0xc7, 0x06, 0x63, 0x96, 0x06, 0x00, 0x13, 0x05, 0x00, 0x00, 0x6f, 0x00, 0x80, + 0x01, 0x03, 0xa5, 0x44, 0x00, 0x93, 0x96, 0x36, 0x00, 0x23, 0x2a, 0xa1, 0x00, 0x23, 0x2e, 0xd1, 0x00, 0x13, 0x05, + 0x80, 0x00, 0x23, 0x2c, 0xa1, 0x00, 0x13, 0x05, 0x81, 0x00, 0x93, 0x05, 0x80, 0x00, 0x93, 0x06, 0x41, 0x01, 0x97, + 0x00, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xec, 0x03, 0x25, 0x81, 0x00, 0x63, 0x1a, 0x05, 0x02, 0x03, 0x25, 0xc1, 0x00, + 0x23, 0xa0, 0x24, 0x01, 0x23, 0xa2, 0xa4, 0x00, 0x83, 0x20, 0xc1, 0x02, 0x03, 0x24, 0x81, 0x02, 0x83, 0x24, 0x41, + 0x02, 0x03, 0x29, 0x01, 0x02, 0x13, 0x01, 0x01, 0x03, 0x67, 0x80, 0x00, 0x00, 0x13, 0x06, 0x04, 0x00, 0x97, 0x10, + 0x00, 0x00, 0xe7, 0x80, 0x80, 0xd5, 0x03, 0x25, 0xc1, 0x00, 0x83, 0x25, 0x01, 0x01, 0x13, 0x06, 0x04, 0x00, 0x97, + 0x10, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xd4, 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, 0x11, 0x02, 0x23, 0x24, 0x81, 0x02, + 0x23, 0x22, 0x91, 0x02, 0x63, 0x0e, 0x07, 0x0c, 0xb3, 0x84, 0xc5, 0x00, 0x63, 0xea, 0xb4, 0x0c, 0x93, 0x87, 0x06, + 0x00, 0x13, 0x04, 0x05, 0x00, 0x83, 0x26, 0x05, 0x00, 0x13, 0x95, 0x16, 0x00, 0x63, 0x64, 0x95, 0x00, 0x93, 0x04, + 0x05, 0x00, 0x13, 0x05, 0x10, 0x40, 0x93, 0x05, 0x10, 0x00, 0x63, 0x68, 0xa7, 0x00, 0x13, 0x05, 0x10, 0x00, 0x63, + 0x08, 0xb7, 0x00, 0x6f, 0x00, 0x00, 0x01, 0x13, 0x05, 0x40, 0x00, 0x63, 0x14, 0xb7, 0x00, 0x13, 0x05, 0x80, 0x00, + 0x63, 0x64, 0x95, 0x00, 0x93, 0x04, 0x05, 0x00, 0x33, 0x85, 0xe7, 0x00, 0x13, 0x05, 0xf5, 0xff, 0xb3, 0x05, 0xf0, + 0x40, 0xb3, 0x75, 0xb5, 0x00, 0x33, 0xb6, 0x95, 0x02, 0x13, 0x05, 0x00, 0x00, 0x63, 0x1c, 0x06, 0x06, 0x33, 0x86, + 0x95, 0x02, 0xb7, 0x05, 0x00, 0x80, 0x33, 0x88, 0xf5, 0x40, 0x63, 0x60, 0xc8, 0x08, 0x63, 0x96, 0x06, 0x00, 0x13, + 0x05, 0x00, 0x00, 0x6f, 0x00, 0x80, 0x01, 0x03, 0x25, 0x44, 0x00, 0xb3, 0x85, 0xe6, 0x02, 0x23, 0x2c, 0xa1, 0x00, + 0x23, 0x20, 0xb1, 0x02, 0x13, 0x85, 0x07, 0x00, 0x23, 0x2e, 0xa1, 0x00, 0x13, 0x05, 0xc1, 0x00, 0x93, 0x06, 0x81, + 0x01, 0x93, 0x85, 0x07, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0xdb, 0x03, 0x25, 0xc1, 0x00, 0x63, 0x1c, + 0x05, 0x02, 0x03, 0x25, 0x01, 0x01, 0x23, 0x20, 0x94, 0x00, 0x23, 0x22, 0xa4, 0x00, 0x83, 0x20, 0xc1, 0x02, 0x03, + 0x24, 0x81, 0x02, 0x83, 0x24, 0x41, 0x02, 0x13, 0x01, 0x01, 0x03, 0x67, 0x80, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00, + 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x62, 0x97, 0x10, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xc4, 0x03, 0x25, 0x01, + 0x01, 0x83, 0x25, 0x41, 0x01, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x62, 0x97, 0x10, 0x00, 0x00, 0xe7, 0x80, + 0xc0, 0xc2, 0x13, 0x01, 0x01, 0xfc, 0x23, 0x2e, 0x11, 0x02, 0x23, 0x2c, 0x81, 0x02, 0x23, 0x2a, 0x91, 0x02, 0x23, + 0x28, 0x21, 0x03, 0x23, 0x26, 0x31, 0x03, 0x23, 0x24, 0x41, 0x03, 0x23, 0x22, 0x51, 0x03, 0x23, 0x20, 0x61, 0x03, + 0x23, 0x2e, 0x71, 0x01, 0x23, 0x2c, 0x81, 0x01, 0x23, 0x2a, 0x91, 0x01, 0x23, 0x28, 0xa1, 0x01, 0x23, 0x26, 0xb1, + 0x01, 0x37, 0xb4, 0x02, 0x00, 0x03, 0x40, 0x54, 0x91, 0x93, 0x04, 0x05, 0x00, 0x13, 0x05, 0x00, 0x0b, 0x93, 0x05, + 0x80, 0x00, 0x97, 0xa0, 0xff, 0xff, 0xe7, 0x80, 0x00, 0xdb, 0xe3, 0x02, 0x05, 0x1e, 0x23, 0x24, 0x91, 0x00, 0xb7, + 0xa5, 0x00, 0x00, 0x93, 0x85, 0x85, 0x63, 0x13, 0x06, 0x00, 0x0b, 0x23, 0x22, 0xa1, 0x00, 0x97, 0x20, 0x00, 0x00, + 0xe7, 0x80, 0x40, 0x8a, 0x03, 0x40, 0x54, 0x91, 0x13, 0x05, 0x00, 0x18, 0x93, 0x05, 0x80, 0x00, 0x97, 0xa0, 0xff, + 0xff, 0xe7, 0x80, 0xc0, 0xd7, 0xe3, 0x0c, 0x05, 0x18, 0x13, 0x09, 0x05, 0x00, 0xb7, 0xef, 0x02, 0xc0, 0x37, 0x1d, + 0x5b, 0x97, 0xb7, 0x28, 0xa5, 0xbc, 0x37, 0x65, 0x14, 0xfe, 0xb7, 0x92, 0x93, 0x72, 0xb7, 0xf9, 0x74, 0xe7, 0x37, + 0x0a, 0xf3, 0xe4, 0xb7, 0x3d, 0x32, 0xe0, 0x37, 0xc3, 0xe0, 0xd5, 0x37, 0x68, 0x1b, 0x54, 0xb7, 0xc5, 0x33, 0xec, + 0xb7, 0x40, 0xcc, 0xf3, 0xb7, 0xd3, 0x6d, 0x48, 0xb7, 0x44, 0x9d, 0x41, 0x37, 0xbe, 0x1c, 0x6c, 0x37, 0x2b, 0xcc, + 0x58, 0xb7, 0x16, 0xf3, 0xe3, 0x37, 0x37, 0x14, 0x9a, 0x37, 0x56, 0xa6, 0xd9, 0xb7, 0x8e, 0xca, 0x8d, 0x37, 0x4f, + 0x9b, 0xe5, 0xb7, 0xa7, 0xbb, 0xaa, 0x37, 0xdc, 0x3d, 0x2a, 0xb7, 0x9b, 0x47, 0x5b, 0xb7, 0xdc, 0x09, 0x55, 0x93, + 0x8f, 0x0f, 0x77, 0x13, 0x04, 0x7d, 0x60, 0x23, 0x20, 0x89, 0x00, 0x13, 0x05, 0xa5, 0x93, 0x23, 0x22, 0xf9, 0x01, + 0xb7, 0x3f, 0xc9, 0xc9, 0x93, 0x88, 0xd8, 0xa8, 0x23, 0x24, 0xa9, 0x00, 0xb7, 0x4a, 0xeb, 0x56, 0x23, 0x26, 0x19, + 0x01, 0x37, 0xdd, 0x56, 0x52, 0x13, 0x85, 0xb2, 0xdf, 0x93, 0x88, 0x99, 0x7f, 0x23, 0x28, 0x19, 0x01, 0xb7, 0xa2, + 0x7a, 0x1f, 0x93, 0x88, 0xcd, 0x4a, 0x23, 0x2a, 0xa9, 0x00, 0xb7, 0xf9, 0x3b, 0x92, 0x13, 0x05, 0x9a, 0xe2, 0x23, + 0x2c, 0x19, 0x01, 0x23, 0x2e, 0xa9, 0x00, 0xb7, 0x8d, 0x7a, 0x39, 0x13, 0x05, 0xf3, 0xa2, 0x13, 0x08, 0x98, 0x44, + 0x23, 0x20, 0x09, 0x03, 0x37, 0x74, 0x07, 0xe5, 0x13, 0x88, 0x10, 0x6c, 0x23, 0x22, 0xa9, 0x02, 0x37, 0xca, 0x72, + 0x5b, 0x13, 0x85, 0x85, 0x86, 0x23, 0x24, 0x09, 0x03, 0x23, 0x26, 0xa9, 0x02, 0xb7, 0x90, 0x60, 0x2c, 0x13, 0x85, + 0x53, 0xb5, 0x93, 0x85, 0xa4, 0x75, 0x23, 0x28, 0xb9, 0x02, 0xb7, 0x63, 0x89, 0xcd, 0x93, 0x05, 0x1b, 0x4f, 0x23, + 0x2a, 0xa9, 0x02, 0x37, 0x85, 0xb5, 0x68, 0x13, 0x08, 0x3e, 0x2a, 0x23, 0x2c, 0xb9, 0x02, 0x23, 0x2e, 0x09, 0x03, + 0xb7, 0xb4, 0x30, 0xd7, 0x93, 0x85, 0x06, 0xd5, 0x93, 0x06, 0xb7, 0x6b, 0x23, 0x20, 0xd9, 0x04, 0x37, 0x3e, 0x3e, + 0xee, 0x93, 0x86, 0x9e, 0xc2, 0x23, 0x22, 0xb9, 0x04, 0x37, 0x9b, 0xbe, 0x9a, 0x93, 0x05, 0x66, 0xf0, 0x23, 0x24, + 0xd9, 0x04, 0x37, 0xb7, 0x9b, 0xd2, 0x23, 0x26, 0xb9, 0x04, 0xb7, 0xc6, 0xb9, 0xbe, 0x93, 0x05, 0x7f, 0xf5, 0x13, + 0x86, 0xe7, 0x1a, 0x23, 0x28, 0xc9, 0x04, 0xb7, 0x1e, 0x5c, 0x7d, 0x13, 0x86, 0xcb, 0xfd, 0x23, 0x2a, 0xb9, 0x04, + 0xb7, 0xeb, 0x08, 0xf6, 0x93, 0x05, 0x0c, 0x45, 0x23, 0x2c, 0xc9, 0x04, 0x23, 0x2e, 0xb9, 0x04, 0x37, 0x88, 0xf0, + 0xaa, 0xb7, 0x05, 0xa2, 0xad, 0x93, 0x85, 0xc5, 0x8d, 0x37, 0xf6, 0x76, 0x76, 0x13, 0x06, 0x56, 0xd2, 0x23, 0x20, + 0xc9, 0x06, 0x37, 0x9f, 0x5a, 0xb3, 0x13, 0x86, 0x6c, 0x51, 0x23, 0x22, 0xb9, 0x06, 0x37, 0xfc, 0x2a, 0x6d, 0xb7, + 0xb5, 0x11, 0x27, 0x93, 0x85, 0xb5, 0xa8, 0x23, 0x24, 0xc9, 0x06, 0x23, 0x26, 0xb9, 0x06, 0xb7, 0x58, 0xaf, 0xaf, + 0xb7, 0xb5, 0xe6, 0x4a, 0x93, 0x85, 0x05, 0xcd, 0x13, 0x86, 0x7f, 0x89, 0x23, 0x28, 0xc9, 0x06, 0xb7, 0x4c, 0xf0, + 0x78, 0x13, 0x06, 0xad, 0x67, 0x23, 0x2a, 0xb9, 0x06, 0xb7, 0x2f, 0x8b, 0xd4, 0x93, 0x85, 0xba, 0xd6, 0x23, 0x2c, + 0xc9, 0x06, 0xb7, 0x9a, 0x52, 0x26, 0x23, 0x2e, 0xb9, 0x06, 0x37, 0xfd, 0x7c, 0xe4, 0x93, 0x85, 0x52, 0xd5, 0x13, + 0x86, 0xe9, 0x51, 0x23, 0x20, 0xc9, 0x08, 0x13, 0x86, 0x8d, 0xf6, 0x23, 0x22, 0xb9, 0x08, 0xb7, 0x45, 0x00, 0x36, + 0x93, 0x85, 0xd5, 0x27, 0x23, 0x24, 0xc9, 0x08, 0x23, 0x26, 0xb9, 0x08, 0x37, 0x03, 0x8e, 0x69, 0x93, 0x05, 0x74, + 0xdf, 0x13, 0x06, 0x0a, 0x3d, 0x23, 0x28, 0xc9, 0x08, 0x13, 0x86, 0xd0, 0x0a, 0x23, 0x2a, 0xb9, 0x08, 0xb7, 0x9d, + 0xae, 0xdf, 0xb7, 0xa5, 0xd5, 0xfc, 0x93, 0x85, 0x15, 0xaa, 0x23, 0x2c, 0xc9, 0x08, 0xb7, 0x40, 0xd5, 0x4f, 0x23, + 0x2e, 0xb9, 0x08, 0xb7, 0xa2, 0x04, 0xca, 0x93, 0x85, 0xc3, 0xe8, 0x13, 0x05, 0xe5, 0xa9, 0x23, 0x20, 0xa9, 0x0a, + 0x37, 0xca, 0x62, 0x57, 0x13, 0x85, 0xe4, 0xe3, 0x23, 0x22, 0xb9, 0x0a, 0xb7, 0x59, 0xf2, 0x81, 0xb7, 0x85, 0xdf, + 0x41, 0x93, 0x85, 0x95, 0xef, 0x23, 0x24, 0xa9, 0x0a, 0xb7, 0x94, 0x15, 0xf5, 0x23, 0x26, 0xb9, 0x0a, 0xb7, 0xe3, + 0xb5, 0xbe, 0x13, 0x05, 0x8e, 0xb8, 0x93, 0x05, 0xdb, 0x77, 0x23, 0x28, 0xb9, 0x0a, 0x37, 0x3e, 0x5e, 0x1e, 0x93, + 0x85, 0x56, 0x40, 0x23, 0x2a, 0xa9, 0x0a, 0xb7, 0x27, 0xe3, 0x60, 0x13, 0x05, 0xd7, 0x7e, 0x23, 0x2c, 0xb9, 0x0a, + 0x37, 0x6b, 0x24, 0x62, 0x23, 0x2e, 0xa9, 0x0a, 0x37, 0xb7, 0xaa, 0xf9, 0x13, 0x85, 0xee, 0x8e, 0x93, 0x85, 0x2b, + 0x38, 0x23, 0x20, 0xb9, 0x0c, 0xb7, 0x5e, 0x28, 0xaa, 0x93, 0x05, 0xc8, 0x02, 0x23, 0x22, 0xa9, 0x0c, 0x37, 0x28, + 0xb2, 0xcd, 0x37, 0x95, 0xae, 0x89, 0x13, 0x05, 0xc5, 0x89, 0x23, 0x24, 0xb9, 0x0c, 0xb7, 0xeb, 0xc8, 0x63, 0x23, + 0x26, 0xa9, 0x0c, 0xb7, 0xf6, 0x2b, 0x54, 0x13, 0x05, 0x7f, 0xe9, 0x13, 0x0f, 0x7c, 0x61, 0x23, 0x28, 0xe9, 0x0d, + 0x37, 0x9f, 0xa5, 0x3b, 0x13, 0x84, 0x38, 0x17, 0x23, 0x2a, 0xa9, 0x0c, 0xb7, 0xb8, 0x43, 0xcb, 0x37, 0x25, 0x14, + 0xdb, 0x13, 0x05, 0xe5, 0x34, 0x23, 0x2c, 0x89, 0x0c, 0x37, 0xac, 0xed, 0x23, 0x23, 0x2e, 0xa9, 0x0c, 0x37, 0x56, + 0x91, 0xc9, 0x13, 0x84, 0x2c, 0x46, 0x93, 0x8f, 0x8f, 0xc9, 0x23, 0x20, 0xf9, 0x0f, 0xb7, 0xcf, 0xd3, 0xaa, 0x13, + 0x0d, 0xad, 0x88, 0x23, 0x22, 0x89, 0x0e, 0xb7, 0xac, 0xac, 0xba, 0x13, 0x84, 0x0a, 0x3b, 0x23, 0x24, 0xa9, 0x0f, + 0x37, 0x9d, 0xda, 0xe9, 0x23, 0x26, 0x89, 0x0e, 0x37, 0x24, 0x94, 0x9b, 0x37, 0x65, 0x9a, 0x99, 0x93, 0x0a, 0xb5, + 0x49, 0x37, 0xd5, 0xb9, 0x69, 0x13, 0x05, 0xf5, 0x32, 0xb7, 0xa5, 0xb0, 0x64, 0x93, 0x85, 0x65, 0x18, 0x13, 0x03, + 0x33, 0x1d, 0x23, 0x28, 0xa9, 0x0e, 0x23, 0x2a, 0x59, 0x0f, 0x23, 0x2c, 0x69, 0x0e, 0x23, 0x2e, 0xb9, 0x0e, 0x37, + 0x43, 0xb1, 0xcd, 0x37, 0x25, 0x0b, 0xee, 0x13, 0x05, 0x05, 0x2d, 0x93, 0x85, 0x8d, 0xbb, 0x93, 0x8a, 0x00, 0xe5, + 0x93, 0x82, 0xe2, 0x7e, 0x23, 0x20, 0xb9, 0x10, 0x23, 0x22, 0xa9, 0x10, 0x23, 0x24, 0x59, 0x10, 0x23, 0x26, 0x59, + 0x11, 0xb7, 0x02, 0x3e, 0xd9, 0x13, 0x0a, 0x1a, 0xfe, 0x13, 0x85, 0x79, 0x04, 0x93, 0x85, 0x24, 0x3e, 0x93, 0x83, + 0xd3, 0x3b, 0x23, 0x28, 0xa9, 0x10, 0x23, 0x2a, 0x49, 0x11, 0x23, 0x2c, 0x79, 0x10, 0x23, 0x2e, 0xb9, 0x10, 0x37, + 0xc5, 0x2c, 0x90, 0x93, 0x05, 0x7e, 0xb5, 0x93, 0x87, 0x77, 0x47, 0x93, 0x03, 0x1b, 0x2a, 0x13, 0x07, 0xd7, 0xee, + 0x23, 0x20, 0xf9, 0x12, 0x23, 0x22, 0xb9, 0x12, 0x23, 0x24, 0xe9, 0x12, 0x23, 0x26, 0x79, 0x12, 0xb7, 0x55, 0xf6, + 0x56, 0x13, 0x87, 0xee, 0xb3, 0x93, 0x07, 0xe8, 0x2a, 0x13, 0x88, 0xfb, 0x72, 0x93, 0x86, 0xc6, 0x3f, 0x23, 0x28, + 0xf9, 0x12, 0x23, 0x2a, 0xe9, 0x12, 0x23, 0x2c, 0xd9, 0x12, 0x23, 0x2e, 0x09, 0x13, 0xb7, 0x46, 0x4e, 0x5b, 0x13, + 0x07, 0xaf, 0x8c, 0x93, 0x87, 0x08, 0x5e, 0x13, 0x08, 0x3c, 0x6f, 0x13, 0x06, 0xd6, 0x0d, 0x23, 0x20, 0xf9, 0x14, + 0x23, 0x22, 0xe9, 0x14, 0x23, 0x24, 0xc9, 0x14, 0x23, 0x26, 0x09, 0x15, 0x37, 0x56, 0x59, 0xc7, 0x13, 0x87, 0x4f, + 0xea, 0x93, 0x87, 0xac, 0xa5, 0x13, 0x08, 0x9d, 0xd6, 0x93, 0x08, 0xa4, 0x84, 0x23, 0x28, 0xf9, 0x14, 0x23, 0x2a, + 0xe9, 0x14, 0x23, 0x2c, 0x19, 0x15, 0x23, 0x2e, 0x09, 0x15, 0x37, 0x67, 0x79, 0xde, 0x93, 0x07, 0xc3, 0xf4, 0x13, + 0x88, 0xc2, 0x24, 0x13, 0x05, 0x95, 0xd0, 0x93, 0x85, 0x35, 0x5e, 0x23, 0x20, 0x09, 0x17, 0x23, 0x22, 0xf9, 0x16, + 0x23, 0x24, 0xb9, 0x16, 0x23, 0x26, 0xa9, 0x16, 0x37, 0x85, 0x35, 0xa2, 0x93, 0x85, 0xf6, 0x0f, 0x13, 0x06, 0xf6, + 0x32, 0x93, 0x06, 0x07, 0xc2, 0x13, 0x05, 0x75, 0xaf, 0x23, 0x28, 0xc9, 0x16, 0x23, 0x2a, 0xb9, 0x16, 0x23, 0x2c, + 0xa9, 0x16, 0x23, 0x2e, 0xd9, 0x16, 0x37, 0xb5, 0x02, 0x00, 0x03, 0x40, 0x55, 0x91, 0x13, 0x05, 0x00, 0x18, 0x93, + 0x05, 0x80, 0x00, 0x97, 0xa0, 0xff, 0xff, 0xe7, 0x80, 0xc0, 0x8d, 0x63, 0x0c, 0x05, 0x4e, 0xb7, 0xc8, 0x72, 0x7b, + 0x37, 0x5f, 0xea, 0xe0, 0xb7, 0x75, 0x45, 0x14, 0xb7, 0xab, 0xce, 0xe2, 0xb7, 0x32, 0x20, 0x80, 0xb7, 0x0f, 0x36, + 0x8f, 0x37, 0xc6, 0x36, 0x6d, 0x37, 0xec, 0x4f, 0x2c, 0x37, 0xd3, 0x05, 0xa2, 0xb7, 0xe9, 0xb9, 0xa9, 0xb7, 0xe6, + 0xe7, 0xf6, 0xb7, 0xcc, 0xa6, 0x1e, 0xb7, 0x93, 0xf7, 0x34, 0x37, 0x9a, 0x25, 0xe5, 0x37, 0x57, 0x46, 0xb0, 0x37, + 0x8d, 0xb9, 0x77, 0x37, 0xce, 0xdd, 0x64, 0x37, 0x04, 0x7e, 0xde, 0xb7, 0xd7, 0xd0, 0x0e, 0xb7, 0x6d, 0x97, 0x27, + 0xb7, 0x3e, 0xbb, 0x1c, 0x37, 0x38, 0x27, 0x11, 0x93, 0x04, 0x40, 0x00, 0x03, 0x2b, 0x81, 0x00, 0x23, 0x20, 0x9b, + 0x00, 0x23, 0x22, 0x2b, 0x01, 0x13, 0x09, 0x60, 0x01, 0x23, 0x24, 0x9b, 0x00, 0x23, 0x26, 0x9b, 0x00, 0x23, 0x28, + 0xab, 0x00, 0x23, 0x2a, 0x9b, 0x00, 0xb7, 0xb0, 0xd0, 0xcf, 0x23, 0x2c, 0x2b, 0x01, 0x83, 0x24, 0x41, 0x00, 0x23, + 0x2e, 0x9b, 0x00, 0xb7, 0x94, 0x5f, 0x60, 0x93, 0x88, 0x98, 0x53, 0x13, 0x0f, 0xef, 0xc6, 0x23, 0x20, 0xe5, 0x01, + 0x37, 0x3f, 0x87, 0x30, 0x93, 0x8a, 0x6b, 0x97, 0x23, 0x22, 0x15, 0x01, 0xb7, 0xf8, 0xd5, 0x3f, 0x93, 0x85, 0xa5, + 0x3d, 0x23, 0x24, 0x55, 0x01, 0xb7, 0x0b, 0x96, 0x7e, 0x23, 0x26, 0xb5, 0x00, 0xb7, 0x25, 0x10, 0xe8, 0x93, 0x82, + 0x62, 0x8b, 0x93, 0x8f, 0x8f, 0xc8, 0x23, 0x28, 0xf5, 0x01, 0xb7, 0x7f, 0x4c, 0xa2, 0x93, 0x0a, 0x2c, 0x7c, 0x23, + 0x2a, 0x55, 0x00, 0xb7, 0xd2, 0xd0, 0xf7, 0x13, 0x06, 0x26, 0x50, 0x23, 0x2c, 0x55, 0x01, 0x37, 0xfc, 0x23, 0xde, + 0x23, 0x2e, 0xc5, 0x00, 0x37, 0x46, 0x99, 0xf3, 0x13, 0x03, 0xf3, 0x0f, 0x93, 0x89, 0x39, 0xef, 0x23, 0x20, 0x35, + 0x03, 0xb7, 0x59, 0x58, 0xa8, 0x93, 0x8a, 0xfc, 0xa2, 0x23, 0x22, 0x65, 0x02, 0x37, 0x93, 0x80, 0x6d, 0x93, 0x86, + 0xb6, 0x38, 0x23, 0x24, 0x55, 0x03, 0xb7, 0x2c, 0xb7, 0xf7, 0x23, 0x26, 0xd5, 0x02, 0xb7, 0x76, 0x80, 0xf8, 0x93, + 0x83, 0xa3, 0x09, 0x13, 0x0a, 0x4a, 0xd6, 0x23, 0x28, 0x45, 0x03, 0x37, 0xaa, 0xf4, 0x88, 0x93, 0x0a, 0xad, 0xfc, + 0x23, 0x2a, 0x75, 0x02, 0xb7, 0x33, 0xdf, 0x57, 0x13, 0x07, 0xd7, 0xd9, 0x23, 0x2c, 0x55, 0x03, 0x37, 0x1d, 0x32, + 0x2a, 0x23, 0x2e, 0xe5, 0x02, 0x37, 0xc7, 0xe7, 0x16, 0x13, 0x0e, 0x5e, 0x9d, 0x13, 0x04, 0x64, 0x0a, 0x23, 0x20, + 0x85, 0x04, 0x37, 0x54, 0x65, 0x4d, 0x93, 0x8a, 0x7d, 0xd9, 0x23, 0x22, 0xc5, 0x05, 0x37, 0xee, 0x3d, 0x21, 0x93, + 0x87, 0xc7, 0x75, 0x23, 0x24, 0x55, 0x05, 0xb7, 0x4d, 0xf3, 0xfc, 0x23, 0x26, 0xf5, 0x04, 0x37, 0xc9, 0xf2, 0xb0, + 0x93, 0x8e, 0x1e, 0x6f, 0x93, 0x0a, 0xb8, 0x38, 0x23, 0x28, 0x55, 0x05, 0xb7, 0x77, 0x09, 0x5a, 0x93, 0x8a, 0xa0, + 0x6b, 0x23, 0x2a, 0xd5, 0x05, 0xb7, 0xde, 0xe3, 0xd8, 0x37, 0xe8, 0x73, 0x66, 0x13, 0x08, 0x58, 0x50, 0x23, 0x2c, + 0x55, 0x05, 0xb7, 0x50, 0x35, 0xbd, 0x23, 0x2e, 0x05, 0x05, 0x37, 0x78, 0x98, 0xe4, 0x93, 0x8a, 0x84, 0x02, 0x13, + 0x0f, 0x1f, 0xe0, 0x23, 0x20, 0xe5, 0x07, 0xb7, 0x34, 0xd9, 0x79, 0x93, 0x8b, 0xfb, 0xe4, 0x23, 0x22, 0x55, 0x07, + 0x37, 0x4f, 0x3b, 0x76, 0x93, 0x88, 0x28, 0xb9, 0x23, 0x24, 0x75, 0x07, 0xb7, 0x2b, 0x82, 0xa3, 0x23, 0x26, 0x15, + 0x07, 0xb7, 0xc8, 0x08, 0x09, 0x93, 0x85, 0x55, 0x5b, 0x93, 0x8f, 0xdf, 0x9c, 0x23, 0x28, 0xf5, 0x07, 0xb7, 0x2f, + 0x0a, 0xeb, 0x93, 0x0a, 0xec, 0x74, 0x23, 0x2a, 0xb5, 0x06, 0xb7, 0xf5, 0x91, 0x76, 0x93, 0x82, 0x52, 0xe7, 0x23, + 0x2c, 0x55, 0x07, 0x37, 0x2c, 0x4b, 0x19, 0x23, 0x2e, 0x55, 0x06, 0xb7, 0x52, 0x43, 0x0e, 0x13, 0x06, 0x66, 0x2b, + 0x93, 0x89, 0x99, 0x08, 0x23, 0x20, 0x35, 0x09, 0xb7, 0x19, 0xae, 0x33, 0x93, 0x8a, 0x6c, 0xdf, 0x23, 0x22, 0xc5, + 0x08, 0x37, 0x96, 0xc3, 0x93, 0x13, 0x03, 0x83, 0xa0, 0x23, 0x24, 0x55, 0x09, 0xb7, 0xec, 0x2f, 0xfc, 0x23, 0x26, + 0x65, 0x08, 0x37, 0x03, 0xf6, 0x98, 0x93, 0x86, 0x56, 0xb6, 0x13, 0x0a, 0xba, 0xa8, 0x23, 0x28, 0x45, 0x09, 0x37, + 0xda, 0x48, 0x5d, 0x93, 0x0a, 0xad, 0x07, 0x23, 0x2a, 0xd5, 0x08, 0xb7, 0x66, 0xd9, 0xe3, 0x93, 0x83, 0xc3, 0xd8, + 0x23, 0x2c, 0x55, 0x09, 0x37, 0x2d, 0x98, 0x30, 0x23, 0x2e, 0x75, 0x08, 0xb7, 0x83, 0xf5, 0x1e, 0x93, 0x0a, 0x77, + 0x20, 0x13, 0x07, 0xd4, 0xa2, 0x23, 0x20, 0xe5, 0x0a, 0x37, 0x77, 0xaf, 0x3e, 0x93, 0x8d, 0x5d, 0x83, 0x23, 0x22, + 0x55, 0x0b, 0x37, 0xc4, 0x24, 0x0b, 0x13, 0x0e, 0x1e, 0x24, 0x23, 0x24, 0xb5, 0x0b, 0xb7, 0x0d, 0x31, 0xa0, 0x23, + 0x26, 0xc5, 0x0b, 0x37, 0x7e, 0x43, 0x62, 0x93, 0x0a, 0x09, 0x89, 0x93, 0x87, 0x67, 0x6f, 0x23, 0x28, 0xf5, 0x0a, + 0xb7, 0xb7, 0x70, 0x56, 0x93, 0x80, 0x70, 0x41, 0x23, 0x2a, 0x55, 0x0b, 0x37, 0x39, 0x1e, 0x5f, 0x93, 0x8e, 0xbe, + 0xf2, 0x23, 0x2c, 0x15, 0x0a, 0xb7, 0xe0, 0x11, 0x5a, 0x23, 0x2e, 0xd5, 0x0b, 0xb7, 0x0e, 0x04, 0xfe, 0x93, 0x0a, + 0x68, 0x91, 0x13, 0x88, 0xf4, 0x30, 0x23, 0x20, 0x05, 0x0d, 0x37, 0x08, 0x9e, 0x90, 0x93, 0x8b, 0x8b, 0xb2, 0x23, + 0x22, 0x55, 0x0d, 0xb7, 0xe4, 0x57, 0x72, 0x13, 0x0f, 0x2f, 0x5d, 0x23, 0x24, 0x75, 0x0d, 0xb7, 0x4b, 0xfd, 0x47, + 0x23, 0x26, 0xe5, 0x0d, 0x37, 0x1f, 0xae, 0xe6, 0x93, 0x8a, 0x58, 0xf6, 0x93, 0x88, 0xdf, 0xf0, 0x23, 0x28, 0x15, + 0x0d, 0xb7, 0x38, 0x2f, 0xb8, 0x13, 0x0c, 0x4c, 0x4f, 0x23, 0x2a, 0x55, 0x0d, 0xb7, 0x0f, 0x12, 0x57, 0x93, 0x85, + 0xd5, 0xb2, 0x23, 0x2c, 0x85, 0x0d, 0x37, 0x4c, 0x78, 0xf4, 0x23, 0x2e, 0xb5, 0x0c, 0xb7, 0x45, 0x3a, 0xf8, 0x93, + 0x8a, 0x22, 0x51, 0x93, 0x82, 0x29, 0x3b, 0x23, 0x20, 0x55, 0x0e, 0xb7, 0xc2, 0x08, 0x0c, 0x93, 0x89, 0xfc, 0x76, + 0x23, 0x22, 0x55, 0x0f, 0xb7, 0xda, 0xd8, 0x2c, 0x13, 0x06, 0xb6, 0x3d, 0x23, 0x24, 0x35, 0x0f, 0xb7, 0xf9, 0x3c, + 0x9a, 0x23, 0x26, 0xc5, 0x0e, 0x37, 0x06, 0x30, 0x7d, 0x13, 0x03, 0x83, 0x74, 0x13, 0x0a, 0xaa, 0xde, 0x23, 0x28, + 0x45, 0x0f, 0x37, 0x7a, 0xe1, 0xb0, 0x93, 0x0c, 0x0d, 0x9c, 0x23, 0x2a, 0x65, 0x0e, 0x37, 0x13, 0xae, 0x85, 0x93, + 0x86, 0x06, 0xf3, 0x23, 0x2c, 0x95, 0x0f, 0xb7, 0x4c, 0xea, 0xca, 0x23, 0x2e, 0xd5, 0x0e, 0xb7, 0x46, 0xa3, 0x52, + 0x93, 0x83, 0x93, 0x1a, 0x13, 0x07, 0xf7, 0xac, 0x13, 0x04, 0x74, 0x1b, 0x13, 0x8d, 0x4d, 0xca, 0x23, 0x20, 0xe5, + 0x10, 0x23, 0x22, 0x75, 0x10, 0x23, 0x24, 0xa5, 0x11, 0x23, 0x26, 0x85, 0x10, 0x37, 0xb7, 0xad, 0xc7, 0x93, 0x03, + 0xee, 0x0b, 0x93, 0x87, 0x77, 0x32, 0x13, 0x0e, 0x19, 0x86, 0x13, 0x84, 0x60, 0x48, 0x23, 0x28, 0xf5, 0x10, 0x23, + 0x2a, 0x75, 0x10, 0x23, 0x2c, 0x85, 0x10, 0x23, 0x2e, 0xc5, 0x11, 0xb7, 0x57, 0x4c, 0x50, 0x93, 0x83, 0xfe, 0x51, + 0x13, 0x08, 0xb8, 0x42, 0x13, 0x8e, 0x14, 0x5b, 0x93, 0x8e, 0x3b, 0x80, 0x23, 0x20, 0x05, 0x13, 0x23, 0x22, 0x75, + 0x12, 0x23, 0x24, 0xd5, 0x13, 0x23, 0x26, 0xc5, 0x13, 0x37, 0xf8, 0xfa, 0xf3, 0x93, 0x03, 0xbf, 0x34, 0x93, 0x88, + 0x88, 0xe7, 0x13, 0x8e, 0xcf, 0xd5, 0x93, 0x0e, 0x1c, 0x51, 0x23, 0x28, 0x15, 0x13, 0x23, 0x2a, 0x75, 0x12, 0x23, + 0x2c, 0xd5, 0x13, 0x23, 0x2e, 0xc5, 0x13, 0xb7, 0x58, 0xe3, 0xbc, 0x93, 0x85, 0x65, 0x26, 0x93, 0x82, 0xc2, 0x0b, + 0x93, 0x83, 0xda, 0x96, 0x13, 0x8e, 0x59, 0x85, 0x23, 0x20, 0x55, 0x14, 0x23, 0x22, 0xb5, 0x14, 0x23, 0x24, 0xc5, + 0x15, 0x23, 0x26, 0x75, 0x14, 0xb7, 0xd5, 0xfc, 0x9c, 0x13, 0x06, 0xb6, 0xb1, 0x93, 0x02, 0x1a, 0x27, 0x13, 0x03, + 0x83, 0x52, 0x93, 0x83, 0x1c, 0x81, 0x23, 0x28, 0x55, 0x14, 0x23, 0x2a, 0xc5, 0x14, 0x23, 0x2c, 0x75, 0x14, 0x23, + 0x2e, 0x65, 0x14, 0x37, 0x46, 0xa3, 0xe0, 0x93, 0x86, 0x56, 0x5d, 0x13, 0x07, 0x87, 0x0b, 0x93, 0x87, 0x17, 0xc5, + 0x13, 0x08, 0x48, 0xe9, 0x23, 0x20, 0xe5, 0x16, 0x23, 0x22, 0xd5, 0x16, 0x23, 0x24, 0x05, 0x17, 0x23, 0x26, 0xf5, + 0x16, 0xb7, 0x76, 0xfb, 0x66, 0x13, 0x87, 0x48, 0xa6, 0x93, 0x85, 0x95, 0xaf, 0x13, 0x06, 0x26, 0x89, 0x93, 0x86, + 0x96, 0xdc, 0x23, 0x28, 0xb5, 0x16, 0x23, 0x2a, 0xe5, 0x16, 0x23, 0x2c, 0xd5, 0x16, 0x23, 0x2e, 0xc5, 0x16, 0x13, + 0x05, 0x60, 0x01, 0x23, 0x20, 0xab, 0x02, 0x83, 0x20, 0xc1, 0x03, 0x03, 0x24, 0x81, 0x03, 0x83, 0x24, 0x41, 0x03, + 0x03, 0x29, 0x01, 0x03, 0x83, 0x29, 0xc1, 0x02, 0x03, 0x2a, 0x81, 0x02, 0x83, 0x2a, 0x41, 0x02, 0x03, 0x2b, 0x01, + 0x02, 0x83, 0x2b, 0xc1, 0x01, 0x03, 0x2c, 0x81, 0x01, 0x83, 0x2c, 0x41, 0x01, 0x03, 0x2d, 0x01, 0x01, 0x83, 0x2d, + 0xc1, 0x00, 0x13, 0x01, 0x01, 0x04, 0x67, 0x80, 0x00, 0x00, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0xc6, 0x76, 0x13, + 0x05, 0x80, 0x00, 0x93, 0x05, 0x00, 0x18, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x1f, 0x37, 0xa6, 0x00, 0x00, + 0x13, 0x06, 0xc6, 0x76, 0x13, 0x05, 0x80, 0x00, 0x93, 0x05, 0x00, 0x0b, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0xc0, + 0x1d, 0x13, 0x05, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2e, + 0x11, 0x00, 0x23, 0x2c, 0x81, 0x00, 0x13, 0x04, 0x01, 0x02, 0x93, 0x05, 0x05, 0x00, 0x37, 0xa5, 0x00, 0x00, 0x13, + 0x05, 0x05, 0x79, 0x13, 0x06, 0x10, 0x00, 0x23, 0x28, 0x04, 0xfe, 0x93, 0x06, 0x40, 0x00, 0x23, 0x20, 0xa4, 0xfe, + 0x23, 0x22, 0xc4, 0xfe, 0x23, 0x24, 0xd4, 0xfe, 0x23, 0x26, 0x04, 0xfe, 0x13, 0x05, 0x04, 0xfe, 0x97, 0x00, 0x00, + 0x00, 0xe7, 0x80, 0x80, 0x29, 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, 0x11, 0x02, 0x23, 0x24, 0x81, 0x02, 0x23, 0x22, + 0x91, 0x02, 0x23, 0x20, 0x21, 0x03, 0x23, 0x2e, 0x31, 0x01, 0x13, 0x04, 0x01, 0x03, 0x13, 0x09, 0x05, 0x00, 0x03, + 0x25, 0x05, 0x00, 0x93, 0x19, 0x15, 0x00, 0x13, 0x06, 0x80, 0x00, 0x93, 0x84, 0x05, 0x00, 0x63, 0x64, 0x36, 0x01, + 0x93, 0x09, 0x80, 0x00, 0x63, 0xc2, 0x09, 0x06, 0x63, 0x0a, 0x05, 0x00, 0x83, 0x25, 0x49, 0x00, 0x23, 0x20, 0xb4, + 0xfe, 0x23, 0x24, 0xa4, 0xfe, 0x13, 0x05, 0x10, 0x00, 0x23, 0x22, 0xa4, 0xfe, 0x13, 0x05, 0x44, 0xfd, 0x93, 0x05, + 0x10, 0x00, 0x93, 0x06, 0x04, 0xfe, 0x13, 0x86, 0x09, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x05, 0x03, + 0x25, 0x44, 0xfd, 0x63, 0x1e, 0x05, 0x02, 0x03, 0x25, 0x84, 0xfd, 0x23, 0x20, 0x39, 0x01, 0x23, 0x22, 0xa9, 0x00, + 0x83, 0x20, 0xc1, 0x02, 0x03, 0x24, 0x81, 0x02, 0x83, 0x24, 0x41, 0x02, 0x03, 0x29, 0x01, 0x02, 0x83, 0x29, 0xc1, + 0x01, 0x13, 0x01, 0x01, 0x03, 0x67, 0x80, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00, 0x13, 0x86, 0x04, 0x00, 0x97, 0x00, + 0x00, 0x00, 0xe7, 0x80, 0x00, 0x0e, 0x03, 0x25, 0x84, 0xfd, 0x83, 0x25, 0xc4, 0xfd, 0x13, 0x86, 0x04, 0x00, 0x97, + 0x00, 0x00, 0x00, 0xe7, 0x80, 0xc0, 0x0c, 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2e, 0x11, 0x00, 0x23, 0x2c, 0x81, 0x00, + 0x23, 0x2a, 0x91, 0x00, 0x23, 0x28, 0x21, 0x01, 0x23, 0x26, 0x31, 0x01, 0x13, 0x04, 0x01, 0x02, 0x93, 0x04, 0x05, + 0x00, 0x63, 0x48, 0x06, 0x06, 0x13, 0x09, 0x06, 0x00, 0x93, 0x89, 0x05, 0x00, 0x03, 0xa5, 0x46, 0x00, 0x63, 0x0a, + 0x05, 0x02, 0x83, 0xa5, 0x86, 0x00, 0x63, 0x86, 0x05, 0x02, 0x03, 0xa5, 0x06, 0x00, 0x13, 0x86, 0x09, 0x00, 0x93, + 0x06, 0x09, 0x00, 0x97, 0x90, 0xff, 0xff, 0xe7, 0x80, 0x00, 0x2a, 0x63, 0x0a, 0x05, 0x02, 0x93, 0x05, 0x00, 0x00, + 0x23, 0xa2, 0xa4, 0x00, 0x23, 0xa4, 0x24, 0x01, 0x6f, 0x00, 0x80, 0x03, 0x63, 0x0a, 0x09, 0x04, 0x37, 0xb5, 0x02, + 0x00, 0x03, 0x40, 0x55, 0x91, 0x13, 0x05, 0x09, 0x00, 0x93, 0x85, 0x09, 0x00, 0x97, 0x90, 0xff, 0xff, 0xe7, 0x80, + 0x40, 0x22, 0xe3, 0x1a, 0x05, 0xfc, 0x23, 0xa2, 0x34, 0x01, 0x23, 0xa4, 0x24, 0x01, 0x6f, 0x00, 0x80, 0x00, 0x23, + 0xa2, 0x04, 0x00, 0x93, 0x05, 0x10, 0x00, 0x23, 0xa0, 0xb4, 0x00, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, + 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, 0x83, 0x29, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, + 0x00, 0x13, 0x85, 0x09, 0x00, 0xe3, 0x9c, 0x09, 0xf8, 0x6f, 0xf0, 0x5f, 0xfc, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, + 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x13, 0x04, 0x01, 0x01, 0x63, 0x18, 0x05, 0x00, 0x13, 0x05, 0x06, 0x00, 0x97, + 0x00, 0x00, 0x00, 0xe7, 0x80, 0x00, 0xe2, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x00, 0x13, 0x01, 0x01, 0xff, + 0x23, 0x26, 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x13, 0x04, 0x01, 0x01, 0x13, 0x06, 0x05, 0x00, 0x13, 0x85, 0x05, + 0x00, 0x93, 0x05, 0x06, 0x00, 0x97, 0xc0, 0xff, 0xff, 0xe7, 0x80, 0x40, 0x43, 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, + 0x11, 0x02, 0x23, 0x24, 0x81, 0x02, 0x13, 0x04, 0x01, 0x03, 0xb7, 0xb5, 0x02, 0x00, 0x83, 0xc5, 0x45, 0x91, 0x23, + 0x2a, 0xa4, 0xfc, 0x13, 0x05, 0x44, 0xfd, 0x63, 0x9a, 0x05, 0x04, 0xb7, 0xa5, 0x00, 0x00, 0x93, 0x85, 0xc5, 0x95, + 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x7d, 0x93, 0x06, 0x20, 0x00, 0x23, 0x24, 0x04, 0xfe, 0x13, 0x07, 0x04, + 0xff, 0x23, 0x28, 0xa4, 0xfe, 0x23, 0x2a, 0xb4, 0xfe, 0x13, 0x05, 0x10, 0x00, 0x23, 0x2c, 0xc4, 0xfc, 0x23, 0x2e, + 0xd4, 0xfc, 0x23, 0x20, 0xe4, 0xfe, 0x23, 0x22, 0xa4, 0xfe, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x7e, 0x13, + 0x05, 0x84, 0xfd, 0x93, 0x05, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x00, 0x08, 0xb7, 0xa5, 0x00, 0x00, + 0x93, 0x85, 0xc5, 0x95, 0x37, 0xa6, 0x00, 0x00, 0x13, 0x06, 0x46, 0x7d, 0x93, 0x06, 0x20, 0x00, 0x23, 0x24, 0x04, + 0xfe, 0x13, 0x07, 0x04, 0xff, 0x23, 0x28, 0xa4, 0xfe, 0x23, 0x2a, 0xb4, 0xfe, 0x13, 0x05, 0x10, 0x00, 0x23, 0x2c, + 0xc4, 0xfc, 0x23, 0x2e, 0xd4, 0xfc, 0x23, 0x20, 0xe4, 0xfe, 0x23, 0x22, 0xa4, 0xfe, 0xb7, 0xa5, 0x00, 0x00, 0x93, + 0x85, 0x45, 0x7f, 0x13, 0x05, 0x84, 0xfd, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x00, 0x13, 0x01, 0x01, 0xfe, + 0x23, 0x2e, 0x11, 0x00, 0x23, 0x2c, 0x81, 0x00, 0x13, 0x04, 0x01, 0x02, 0x13, 0x06, 0x10, 0x00, 0x23, 0x26, 0xa4, + 0xfe, 0x23, 0x28, 0xb4, 0xfe, 0x23, 0x1a, 0xc4, 0xfe, 0x13, 0x05, 0xc4, 0xfe, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, + 0xc0, 0xcf, 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, 0x11, 0x02, 0x23, 0x24, 0x81, 0x02, 0x13, 0x04, 0x01, 0x03, 0x83, + 0x26, 0x05, 0x01, 0x03, 0x27, 0x45, 0x01, 0x83, 0x27, 0x05, 0x00, 0x03, 0x28, 0x45, 0x00, 0x83, 0x28, 0x85, 0x00, + 0x03, 0x25, 0xc5, 0x00, 0x23, 0x22, 0xd4, 0xfe, 0x23, 0x24, 0xe4, 0xfe, 0x93, 0x06, 0x44, 0xfd, 0x23, 0x2a, 0xf4, + 0xfc, 0x23, 0x2c, 0x04, 0xfd, 0x23, 0x2e, 0x14, 0xfd, 0x23, 0x20, 0xa4, 0xfe, 0x23, 0x26, 0xd4, 0xfe, 0x23, 0x28, + 0xc4, 0xfe, 0x23, 0x0a, 0x04, 0xfe, 0xa3, 0x0a, 0xb4, 0xfe, 0x13, 0x05, 0xc4, 0xfe, 0x97, 0x00, 0x00, 0x00, 0xe7, + 0x80, 0xc0, 0xc9, 0x13, 0x01, 0x01, 0xfd, 0x23, 0x26, 0x11, 0x02, 0x23, 0x24, 0x81, 0x02, 0x13, 0x04, 0x01, 0x03, + 0x23, 0x28, 0xa4, 0xfe, 0x23, 0x2a, 0xb4, 0xfe, 0x13, 0x05, 0x04, 0xff, 0x93, 0x05, 0x10, 0x00, 0x23, 0x24, 0x04, + 0xfe, 0x93, 0x06, 0x40, 0x00, 0x23, 0x2c, 0xa4, 0xfc, 0x23, 0x2e, 0xb4, 0xfc, 0x23, 0x20, 0xd4, 0xfe, 0x23, 0x22, + 0x04, 0xfe, 0x13, 0x05, 0x84, 0xfd, 0x93, 0x05, 0x06, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x40, 0xf3, 0x13, + 0x01, 0x01, 0xfc, 0x23, 0x2e, 0x11, 0x02, 0x23, 0x2c, 0x81, 0x02, 0x13, 0x04, 0x01, 0x04, 0x23, 0x24, 0xa4, 0xfc, + 0x23, 0x26, 0xb4, 0xfc, 0x13, 0x05, 0xc4, 0xfc, 0xb7, 0xa5, 0x00, 0x00, 0x93, 0x85, 0xc5, 0x95, 0x93, 0x06, 0x84, + 0xfc, 0x37, 0xb7, 0x00, 0x00, 0x13, 0x07, 0x87, 0x83, 0x93, 0x07, 0x20, 0x00, 0x23, 0x20, 0x04, 0xfe, 0x23, 0x24, + 0xa4, 0xfe, 0x23, 0x26, 0xb4, 0xfe, 0x23, 0x28, 0xd4, 0xfe, 0x23, 0x2a, 0xb4, 0xfe, 0x13, 0x05, 0x84, 0xfe, 0x23, + 0x28, 0xe4, 0xfc, 0x23, 0x2a, 0xf4, 0xfc, 0x23, 0x2c, 0xa4, 0xfc, 0x23, 0x2e, 0xf4, 0xfc, 0x13, 0x05, 0x04, 0xfd, + 0x93, 0x05, 0x06, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x80, 0xec, 0x13, 0x01, 0x01, 0xfc, 0x23, 0x2e, 0x11, + 0x02, 0x23, 0x2c, 0x81, 0x02, 0x23, 0x2a, 0x91, 0x02, 0x23, 0x28, 0x21, 0x03, 0x23, 0x26, 0x31, 0x03, 0x23, 0x24, + 0x41, 0x03, 0x23, 0x22, 0x51, 0x03, 0x23, 0x20, 0x61, 0x03, 0x23, 0x2e, 0x71, 0x01, 0x23, 0x2c, 0x81, 0x01, 0x23, + 0x2a, 0x91, 0x01, 0x23, 0x28, 0xa1, 0x01, 0x23, 0x26, 0xb1, 0x01, 0x13, 0x04, 0x01, 0x04, 0x93, 0x84, 0x07, 0x00, + 0x13, 0x09, 0x07, 0x00, 0x13, 0x8a, 0x06, 0x00, 0x93, 0x0a, 0x06, 0x00, 0x93, 0x09, 0x05, 0x00, 0x63, 0x8c, 0x05, + 0x08, 0x83, 0xab, 0x89, 0x00, 0x37, 0x05, 0x20, 0x00, 0x33, 0xf5, 0xab, 0x00, 0x37, 0x0b, 0x11, 0x00, 0x63, 0x04, + 0x05, 0x00, 0x13, 0x0b, 0xb0, 0x02, 0x13, 0x55, 0x55, 0x01, 0xb3, 0x0c, 0x95, 0x00, 0x13, 0x95, 0x8b, 0x00, 0x63, + 0x52, 0x05, 0x08, 0x13, 0x05, 0x00, 0x01, 0x63, 0x7e, 0xaa, 0x0e, 0x13, 0x05, 0x00, 0x00, 0x63, 0x02, 0x0a, 0x02, + 0xb3, 0x85, 0x4a, 0x01, 0x13, 0x86, 0x0a, 0x00, 0x83, 0x06, 0x06, 0x00, 0x13, 0x06, 0x16, 0x00, 0x93, 0xa6, 0x06, + 0xfc, 0x93, 0xc6, 0x16, 0x00, 0x33, 0x05, 0xd5, 0x00, 0xe3, 0x16, 0xb6, 0xfe, 0xb3, 0x0c, 0x95, 0x01, 0x83, 0xdd, + 0xc9, 0x00, 0x63, 0xfa, 0xbc, 0x05, 0x13, 0x95, 0x7b, 0x00, 0x63, 0x40, 0x05, 0x0e, 0x33, 0x86, 0x9d, 0x41, 0x13, + 0x95, 0x1b, 0x00, 0x13, 0x55, 0xe5, 0x01, 0x93, 0x05, 0x10, 0x00, 0x93, 0x9b, 0xbb, 0x00, 0x23, 0x24, 0x24, 0xfd, + 0x63, 0xc6, 0xa5, 0x14, 0x63, 0x16, 0x05, 0x18, 0x13, 0x0d, 0x00, 0x00, 0x6f, 0x00, 0x80, 0x18, 0x83, 0xab, 0x89, + 0x00, 0x93, 0x8c, 0x14, 0x00, 0x13, 0x0b, 0xd0, 0x02, 0x13, 0x95, 0x8b, 0x00, 0xe3, 0x42, 0x05, 0xf8, 0x93, 0x0a, + 0x00, 0x00, 0x83, 0xdd, 0xc9, 0x00, 0xe3, 0xea, 0xbc, 0xfb, 0x83, 0xab, 0x09, 0x00, 0x83, 0xa9, 0x49, 0x00, 0x13, + 0x85, 0x0b, 0x00, 0x93, 0x85, 0x09, 0x00, 0x13, 0x06, 0x0b, 0x00, 0x93, 0x86, 0x0a, 0x00, 0x13, 0x07, 0x0a, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x24, 0x63, 0x10, 0x05, 0x18, 0x03, 0xa3, 0xc9, 0x00, 0x13, 0x85, 0x0b, + 0x00, 0x93, 0x05, 0x09, 0x00, 0x13, 0x86, 0x04, 0x00, 0x83, 0x20, 0xc1, 0x03, 0x03, 0x24, 0x81, 0x03, 0x83, 0x24, + 0x41, 0x03, 0x03, 0x29, 0x01, 0x03, 0x83, 0x29, 0xc1, 0x02, 0x03, 0x2a, 0x81, 0x02, 0x83, 0x2a, 0x41, 0x02, 0x03, + 0x2b, 0x01, 0x02, 0x83, 0x2b, 0xc1, 0x01, 0x03, 0x2c, 0x81, 0x01, 0x83, 0x2c, 0x41, 0x01, 0x03, 0x2d, 0x01, 0x01, + 0x83, 0x2d, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x04, 0x67, 0x00, 0x03, 0x00, 0x13, 0x85, 0x0a, 0x00, 0x93, 0x05, 0x0a, + 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x40, 0x29, 0xb3, 0x0c, 0x95, 0x01, 0x83, 0xdd, 0xc9, 0x00, 0xe3, 0xfa, + 0xbc, 0xf7, 0x6f, 0xf0, 0x1f, 0xf2, 0x83, 0xab, 0x09, 0x00, 0x03, 0xac, 0x49, 0x00, 0x03, 0xad, 0x89, 0x00, 0x03, + 0xa5, 0xc9, 0x00, 0x23, 0x22, 0xa4, 0xfc, 0x37, 0x05, 0xe0, 0x9f, 0xb7, 0x05, 0x00, 0x20, 0x33, 0x75, 0xad, 0x00, + 0x93, 0x85, 0x05, 0x03, 0x33, 0x65, 0xb5, 0x00, 0x23, 0xa4, 0xa9, 0x00, 0x13, 0x85, 0x0b, 0x00, 0x93, 0x05, 0x0c, + 0x00, 0x13, 0x06, 0x0b, 0x00, 0x93, 0x86, 0x0a, 0x00, 0x13, 0x07, 0x0a, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, + 0x00, 0x19, 0x13, 0x0a, 0x10, 0x00, 0x63, 0x14, 0x05, 0x0c, 0x93, 0x0a, 0x00, 0x00, 0x33, 0x85, 0x9d, 0x41, 0x37, + 0x0b, 0x01, 0x00, 0x13, 0x0b, 0xfb, 0xff, 0xb3, 0x7c, 0x65, 0x01, 0x33, 0xf5, 0x6a, 0x01, 0x63, 0x7c, 0x95, 0x03, + 0x03, 0x26, 0x0c, 0x01, 0x93, 0x8a, 0x1a, 0x00, 0x93, 0x05, 0x00, 0x03, 0x13, 0x85, 0x0b, 0x00, 0xe7, 0x00, 0x06, + 0x00, 0xe3, 0x02, 0x05, 0xfe, 0x6f, 0x00, 0x00, 0x09, 0x93, 0x05, 0x20, 0x00, 0x13, 0x0d, 0x06, 0x00, 0x63, 0x10, + 0xb5, 0x04, 0x13, 0x15, 0x06, 0x01, 0x13, 0x5d, 0x15, 0x01, 0x6f, 0x00, 0x40, 0x03, 0x83, 0x26, 0xcc, 0x00, 0x13, + 0x85, 0x0b, 0x00, 0x93, 0x05, 0x09, 0x00, 0x13, 0x86, 0x04, 0x00, 0xe7, 0x80, 0x06, 0x00, 0x63, 0x10, 0x05, 0x06, + 0x13, 0x0a, 0x00, 0x00, 0x23, 0xa4, 0xa9, 0x01, 0x03, 0x25, 0x44, 0xfc, 0x23, 0xa6, 0xa9, 0x00, 0x6f, 0x00, 0xc0, + 0x04, 0x13, 0x0d, 0x06, 0x00, 0x23, 0x22, 0xc4, 0xfc, 0x93, 0x0d, 0x00, 0x00, 0x93, 0xdb, 0xbb, 0x00, 0x03, 0xac, + 0x09, 0x00, 0x83, 0xa9, 0x49, 0x00, 0x37, 0x09, 0x01, 0x00, 0x13, 0x09, 0xf9, 0xff, 0xb3, 0x7c, 0x2d, 0x01, 0x33, + 0xf5, 0x2d, 0x01, 0x63, 0x70, 0x95, 0x07, 0x03, 0xa6, 0x09, 0x01, 0x93, 0x8d, 0x1d, 0x00, 0x13, 0x05, 0x0c, 0x00, + 0x93, 0x85, 0x0b, 0x00, 0xe7, 0x00, 0x06, 0x00, 0xe3, 0x02, 0x05, 0xfe, 0x13, 0x0a, 0x10, 0x00, 0x13, 0x05, 0x0a, + 0x00, 0x83, 0x20, 0xc1, 0x03, 0x03, 0x24, 0x81, 0x03, 0x83, 0x24, 0x41, 0x03, 0x03, 0x29, 0x01, 0x03, 0x83, 0x29, + 0xc1, 0x02, 0x03, 0x2a, 0x81, 0x02, 0x83, 0x2a, 0x41, 0x02, 0x03, 0x2b, 0x01, 0x02, 0x83, 0x2b, 0xc1, 0x01, 0x03, + 0x2c, 0x81, 0x01, 0x83, 0x2c, 0x41, 0x01, 0x03, 0x2d, 0x01, 0x01, 0x83, 0x2d, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x04, + 0x67, 0x80, 0x00, 0x00, 0x13, 0x05, 0x0c, 0x00, 0x93, 0x85, 0x09, 0x00, 0x13, 0x06, 0x0b, 0x00, 0x93, 0x86, 0x0a, + 0x00, 0x13, 0x07, 0x0a, 0x00, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0x80, 0x06, 0x13, 0x0a, 0x10, 0x00, 0xe3, 0x10, + 0x05, 0xfa, 0x83, 0xa6, 0xc9, 0x00, 0x13, 0x05, 0x0c, 0x00, 0x83, 0x25, 0x84, 0xfc, 0x13, 0x86, 0x04, 0x00, 0xe7, + 0x80, 0x06, 0x00, 0xe3, 0x14, 0x05, 0xf8, 0x93, 0x04, 0x00, 0x00, 0x03, 0x25, 0x44, 0xfc, 0x33, 0x05, 0xa5, 0x41, + 0x37, 0x09, 0x01, 0x00, 0x13, 0x09, 0xf9, 0xff, 0xb3, 0x7a, 0x25, 0x01, 0x33, 0xf5, 0x24, 0x01, 0x33, 0x3a, 0x55, + 0x01, 0xe3, 0x72, 0x55, 0xf7, 0x03, 0xa6, 0x09, 0x01, 0x93, 0x84, 0x14, 0x00, 0x13, 0x05, 0x0c, 0x00, 0x93, 0x85, + 0x0b, 0x00, 0xe7, 0x00, 0x06, 0x00, 0xe3, 0x00, 0x05, 0xfe, 0x6f, 0xf0, 0x9f, 0xf4, 0x13, 0x01, 0x01, 0xfe, 0x23, + 0x2e, 0x11, 0x00, 0x23, 0x2c, 0x81, 0x00, 0x23, 0x2a, 0x91, 0x00, 0x23, 0x28, 0x21, 0x01, 0x23, 0x26, 0x31, 0x01, + 0x23, 0x24, 0x41, 0x01, 0x13, 0x04, 0x01, 0x02, 0x93, 0x04, 0x07, 0x00, 0x13, 0x89, 0x06, 0x00, 0x93, 0x89, 0x05, + 0x00, 0xb7, 0x05, 0x11, 0x00, 0x63, 0x04, 0xb6, 0x02, 0x83, 0xa6, 0x09, 0x01, 0x13, 0x0a, 0x05, 0x00, 0x93, 0x05, + 0x06, 0x00, 0xe7, 0x80, 0x06, 0x00, 0x93, 0x05, 0x05, 0x00, 0x13, 0x05, 0x0a, 0x00, 0x63, 0x86, 0x05, 0x00, 0x13, + 0x05, 0x10, 0x00, 0x6f, 0x00, 0x80, 0x03, 0x63, 0x08, 0x09, 0x02, 0x03, 0xa3, 0xc9, 0x00, 0x93, 0x05, 0x09, 0x00, + 0x13, 0x86, 0x04, 0x00, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, + 0x01, 0x83, 0x29, 0xc1, 0x00, 0x03, 0x2a, 0x81, 0x00, 0x13, 0x01, 0x01, 0x02, 0x67, 0x00, 0x03, 0x00, 0x13, 0x05, + 0x00, 0x00, 0x83, 0x20, 0xc1, 0x01, 0x03, 0x24, 0x81, 0x01, 0x83, 0x24, 0x41, 0x01, 0x03, 0x29, 0x01, 0x01, 0x83, + 0x29, 0xc1, 0x00, 0x03, 0x2a, 0x81, 0x00, 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, + 0x23, 0x26, 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x23, 0x22, 0x91, 0x00, 0x13, 0x04, 0x01, 0x01, 0x13, 0x06, 0x05, + 0x00, 0x13, 0x05, 0x35, 0x00, 0x13, 0x75, 0xc5, 0xff, 0xb3, 0x08, 0xc5, 0x40, 0x63, 0xfe, 0x15, 0x03, 0x13, 0x05, + 0x00, 0x00, 0x63, 0x80, 0x05, 0x02, 0xb3, 0x05, 0xb6, 0x00, 0x83, 0x06, 0x06, 0x00, 0x13, 0x06, 0x16, 0x00, 0x93, + 0xa6, 0x06, 0xfc, 0x93, 0xc6, 0x16, 0x00, 0x33, 0x05, 0xd5, 0x00, 0xe3, 0x16, 0xb6, 0xfe, 0x83, 0x20, 0xc1, 0x00, + 0x03, 0x24, 0x81, 0x00, 0x83, 0x24, 0x41, 0x00, 0x13, 0x01, 0x01, 0x01, 0x67, 0x80, 0x00, 0x00, 0xb3, 0x86, 0x15, + 0x41, 0x13, 0xd8, 0x26, 0x00, 0xe3, 0x00, 0x08, 0xfc, 0xb3, 0x08, 0x16, 0x01, 0x93, 0xf5, 0x36, 0x00, 0x63, 0x16, + 0xc5, 0x00, 0x13, 0x05, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x02, 0x13, 0x05, 0x00, 0x00, 0x03, 0x07, 0x06, 0x00, 0x13, + 0x06, 0x16, 0x00, 0x13, 0x27, 0x07, 0xfc, 0x13, 0x47, 0x17, 0x00, 0x33, 0x05, 0xe5, 0x00, 0xe3, 0x16, 0x16, 0xff, + 0x13, 0x06, 0x00, 0x00, 0x63, 0x84, 0x05, 0x02, 0x93, 0xf6, 0xc6, 0xff, 0xb3, 0x86, 0xd8, 0x00, 0x03, 0x87, 0x06, + 0x00, 0x93, 0x85, 0xf5, 0xff, 0x13, 0x27, 0x07, 0xfc, 0x13, 0x47, 0x17, 0x00, 0x33, 0x06, 0xe6, 0x00, 0x93, 0x86, + 0x16, 0x00, 0xe3, 0x94, 0x05, 0xfe, 0xb7, 0x05, 0x01, 0x01, 0xb7, 0x06, 0xff, 0x00, 0x33, 0x05, 0xa6, 0x00, 0x13, + 0x86, 0x15, 0x10, 0x93, 0x85, 0xf6, 0x0f, 0x13, 0x07, 0x40, 0x00, 0x6f, 0x00, 0x40, 0x03, 0xb3, 0x88, 0x17, 0x01, + 0x33, 0x88, 0x56, 0x40, 0x93, 0xf3, 0x32, 0x00, 0x33, 0x7e, 0xb3, 0x00, 0x13, 0x53, 0x83, 0x00, 0x33, 0x73, 0xb3, + 0x00, 0x33, 0x03, 0xc3, 0x01, 0x13, 0x1e, 0x03, 0x01, 0x33, 0x03, 0x6e, 0x00, 0x13, 0x53, 0x03, 0x01, 0x33, 0x05, + 0xa3, 0x00, 0x63, 0x9a, 0x03, 0x0a, 0xe3, 0x0c, 0x08, 0xf2, 0x93, 0x06, 0x08, 0x00, 0x93, 0x87, 0x08, 0x00, 0x13, + 0x08, 0x00, 0x0c, 0x93, 0x82, 0x06, 0x00, 0x63, 0xe4, 0x06, 0x01, 0x93, 0x02, 0x00, 0x0c, 0x93, 0x98, 0x22, 0x00, + 0x13, 0x03, 0x00, 0x00, 0xe3, 0xe6, 0xe6, 0xfa, 0x13, 0xf8, 0x08, 0x3f, 0x33, 0x88, 0x07, 0x01, 0x93, 0x83, 0x07, + 0x00, 0x03, 0xae, 0x03, 0x00, 0x83, 0xae, 0x43, 0x00, 0x03, 0xaf, 0x83, 0x00, 0x83, 0xaf, 0xc3, 0x00, 0x93, 0x44, + 0xfe, 0xff, 0x13, 0x5e, 0x6e, 0x00, 0x93, 0xd4, 0x74, 0x00, 0x33, 0xee, 0xc4, 0x01, 0x93, 0xc4, 0xfe, 0xff, 0x93, + 0xde, 0x6e, 0x00, 0x93, 0xd4, 0x74, 0x00, 0xb3, 0xee, 0xd4, 0x01, 0x93, 0x44, 0xff, 0xff, 0x13, 0x5f, 0x6f, 0x00, + 0x93, 0xd4, 0x74, 0x00, 0x33, 0xef, 0xe4, 0x01, 0x93, 0xc4, 0xff, 0xff, 0x93, 0xdf, 0x6f, 0x00, 0x93, 0xd4, 0x74, + 0x00, 0xb3, 0xef, 0xf4, 0x01, 0x33, 0x7e, 0xce, 0x00, 0x33, 0x03, 0x6e, 0x00, 0x33, 0xfe, 0xce, 0x00, 0xb3, 0x7e, + 0xcf, 0x00, 0x33, 0xff, 0xcf, 0x00, 0x33, 0x8e, 0xce, 0x01, 0x33, 0x03, 0x6e, 0x00, 0x93, 0x83, 0x03, 0x01, 0x33, + 0x03, 0x6f, 0x00, 0xe3, 0x96, 0x03, 0xf9, 0x6f, 0xf0, 0x5f, 0xf2, 0x13, 0x07, 0x00, 0x00, 0x13, 0xf8, 0xc2, 0x0f, + 0x13, 0x18, 0x28, 0x00, 0xb3, 0x87, 0x07, 0x01, 0x13, 0xb8, 0x06, 0x0c, 0x33, 0x08, 0x00, 0x41, 0xb3, 0xf6, 0x06, + 0x01, 0x93, 0xf6, 0x36, 0x00, 0x93, 0x96, 0x26, 0x00, 0x03, 0xa8, 0x07, 0x00, 0x93, 0x87, 0x47, 0x00, 0x93, 0x48, + 0xf8, 0xff, 0x13, 0x58, 0x68, 0x00, 0x93, 0xd8, 0x78, 0x00, 0x33, 0xe8, 0x08, 0x01, 0x33, 0x78, 0xc8, 0x00, 0x93, + 0x86, 0xc6, 0xff, 0x33, 0x07, 0xe8, 0x00, 0xe3, 0x9e, 0x06, 0xfc, 0x33, 0x76, 0xb7, 0x00, 0x13, 0x57, 0x87, 0x00, + 0xb3, 0x75, 0xb7, 0x00, 0xb3, 0x85, 0xc5, 0x00, 0x13, 0x96, 0x05, 0x01, 0xb3, 0x05, 0xb6, 0x00, 0x93, 0xd5, 0x05, + 0x01, 0x33, 0x85, 0xa5, 0x00, 0x83, 0x20, 0xc1, 0x00, 0x03, 0x24, 0x81, 0x00, 0x83, 0x24, 0x41, 0x00, 0x13, 0x01, + 0x01, 0x01, 0x67, 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x13, + 0x04, 0x01, 0x01, 0x03, 0x25, 0x05, 0x00, 0x13, 0x86, 0x05, 0x00, 0x93, 0x05, 0x10, 0x00, 0x83, 0x20, 0xc1, 0x00, + 0x03, 0x24, 0x81, 0x00, 0x13, 0x01, 0x01, 0x01, 0x17, 0x03, 0x00, 0x00, 0x67, 0x00, 0x83, 0x00, 0x13, 0x01, 0x01, + 0xfc, 0x23, 0x2e, 0x11, 0x02, 0x23, 0x2c, 0x81, 0x02, 0x23, 0x2a, 0x91, 0x02, 0x23, 0x28, 0x21, 0x03, 0x23, 0x26, + 0x31, 0x03, 0x23, 0x24, 0x41, 0x03, 0x23, 0x22, 0x51, 0x03, 0x23, 0x20, 0x61, 0x03, 0x23, 0x2e, 0x71, 0x01, 0x23, + 0x2c, 0x81, 0x01, 0x23, 0x2a, 0x91, 0x01, 0x13, 0x04, 0x01, 0x04, 0x93, 0x06, 0x06, 0x00, 0x13, 0x07, 0x80, 0x3e, + 0x37, 0xb6, 0x00, 0x00, 0x13, 0x06, 0x86, 0x84, 0x63, 0x66, 0xe5, 0x0c, 0x93, 0x07, 0x00, 0x00, 0x13, 0x08, 0x04, + 0xfd, 0x93, 0x08, 0x34, 0xfd, 0x93, 0x02, 0x24, 0xfd, 0x13, 0x03, 0x14, 0xfd, 0x37, 0x17, 0xb7, 0xd1, 0xb7, 0x2e, + 0x00, 0x00, 0x37, 0x1f, 0x00, 0x00, 0x93, 0x03, 0x40, 0x06, 0xb7, 0x9f, 0x98, 0x00, 0x13, 0x0e, 0x97, 0x75, 0x93, + 0x8e, 0x0e, 0x71, 0x13, 0x0f, 0xbf, 0x47, 0x93, 0x8f, 0xff, 0x67, 0x13, 0x07, 0x05, 0x00, 0x93, 0x04, 0x07, 0x00, + 0x33, 0x37, 0xc7, 0x03, 0x33, 0x09, 0xf8, 0x00, 0xb3, 0x09, 0xf3, 0x00, 0x33, 0x8a, 0xf2, 0x00, 0xb3, 0x8a, 0xf8, + 0x00, 0x13, 0x57, 0xd7, 0x00, 0x33, 0x0b, 0xd7, 0x03, 0x33, 0x8b, 0x64, 0x41, 0x93, 0x1b, 0x0b, 0x01, 0x93, 0xdb, + 0x2b, 0x01, 0xb3, 0x8b, 0xeb, 0x03, 0x13, 0xdc, 0x0b, 0x01, 0x93, 0xdb, 0x1b, 0x01, 0xb3, 0x8b, 0x7b, 0x02, 0x13, + 0x7c, 0xec, 0x7f, 0x33, 0x0b, 0x7b, 0x41, 0x33, 0x0c, 0x86, 0x01, 0x13, 0x1b, 0x1b, 0x01, 0x13, 0x5b, 0x0b, 0x01, + 0x33, 0x0b, 0x66, 0x01, 0x83, 0x4b, 0x0c, 0x00, 0x03, 0x4c, 0x1c, 0x00, 0x83, 0x4c, 0x0b, 0x00, 0x03, 0x4b, 0x1b, + 0x00, 0x23, 0x00, 0x79, 0x01, 0x23, 0x80, 0x89, 0x01, 0x23, 0x00, 0x9a, 0x01, 0x23, 0x80, 0x6a, 0x01, 0x93, 0x87, + 0xc7, 0xff, 0xe3, 0xe4, 0x9f, 0xf8, 0x13, 0x88, 0xa7, 0x00, 0x93, 0x07, 0x90, 0x00, 0x63, 0xec, 0xe7, 0x00, 0x6f, + 0x00, 0x80, 0x06, 0x13, 0x08, 0xa0, 0x00, 0x13, 0x07, 0x05, 0x00, 0x93, 0x07, 0x90, 0x00, 0x63, 0xfc, 0xa7, 0x04, + 0x93, 0x17, 0x07, 0x01, 0xb7, 0x18, 0x00, 0x00, 0x93, 0x02, 0x40, 0x06, 0x13, 0x03, 0xa4, 0xfc, 0x93, 0xd7, 0x27, + 0x01, 0x93, 0x88, 0xb8, 0x47, 0xb3, 0x03, 0x03, 0x01, 0xb3, 0x87, 0x17, 0x03, 0x93, 0xd7, 0x17, 0x01, 0xb3, 0x88, + 0x57, 0x02, 0x33, 0x07, 0x17, 0x41, 0x13, 0x17, 0x17, 0x01, 0x13, 0x57, 0x07, 0x01, 0x33, 0x07, 0xe6, 0x00, 0x83, + 0x48, 0x07, 0x00, 0x03, 0x47, 0x17, 0x00, 0x13, 0x08, 0xe8, 0xff, 0x33, 0x03, 0x03, 0x01, 0x23, 0x00, 0x13, 0x01, + 0xa3, 0x8f, 0xe3, 0xfe, 0x13, 0x87, 0x07, 0x00, 0x63, 0x04, 0x05, 0x00, 0x63, 0x02, 0x07, 0x02, 0x13, 0x17, 0x17, + 0x00, 0x13, 0x77, 0xe7, 0x01, 0x33, 0x06, 0xe6, 0x00, 0x03, 0x45, 0x16, 0x00, 0x13, 0x08, 0xf8, 0xff, 0x13, 0x06, + 0xa4, 0xfc, 0x33, 0x06, 0x06, 0x01, 0x23, 0x00, 0xa6, 0x00, 0x13, 0x05, 0xa0, 0x00, 0x13, 0x07, 0xa4, 0xfc, 0xb3, + 0x07, 0x05, 0x41, 0x33, 0x07, 0x07, 0x01, 0x13, 0x06, 0x10, 0x00, 0x13, 0x85, 0x06, 0x00, 0x93, 0x06, 0x00, 0x00, + 0x97, 0xf0, 0xff, 0xff, 0xe7, 0x80, 0xc0, 0x7b, 0x83, 0x20, 0xc1, 0x03, 0x03, 0x24, 0x81, 0x03, 0x83, 0x24, 0x41, + 0x03, 0x03, 0x29, 0x01, 0x03, 0x83, 0x29, 0xc1, 0x02, 0x03, 0x2a, 0x81, 0x02, 0x83, 0x2a, 0x41, 0x02, 0x03, 0x2b, + 0x01, 0x02, 0x83, 0x2b, 0xc1, 0x01, 0x03, 0x2c, 0x81, 0x01, 0x83, 0x2c, 0x41, 0x01, 0x13, 0x01, 0x01, 0x04, 0x67, + 0x80, 0x00, 0x00, 0x13, 0x01, 0x01, 0xfe, 0x23, 0x2e, 0x11, 0x00, 0x23, 0x2c, 0x81, 0x00, 0x13, 0x04, 0x01, 0x02, + 0x93, 0x06, 0x00, 0x01, 0x63, 0x60, 0xd6, 0x08, 0xb3, 0x06, 0xa0, 0x40, 0x93, 0xf6, 0x36, 0x00, 0xb3, 0x07, 0xd5, + 0x00, 0x63, 0x74, 0xf5, 0x02, 0x13, 0x87, 0x06, 0x00, 0x13, 0x08, 0x05, 0x00, 0x93, 0x88, 0x05, 0x00, 0x83, 0xc2, + 0x08, 0x00, 0x13, 0x07, 0xf7, 0xff, 0x23, 0x00, 0x58, 0x00, 0x13, 0x08, 0x18, 0x00, 0x93, 0x88, 0x18, 0x00, 0xe3, + 0x16, 0x07, 0xfe, 0xb3, 0x85, 0xd5, 0x00, 0x33, 0x06, 0xd6, 0x40, 0x13, 0x77, 0xc6, 0xff, 0x93, 0xf8, 0x35, 0x00, + 0xb3, 0x86, 0xe7, 0x00, 0x63, 0x94, 0x08, 0x06, 0x63, 0xfe, 0xd7, 0x00, 0x13, 0x88, 0x05, 0x00, 0x83, 0x28, 0x08, + 0x00, 0x23, 0xa0, 0x17, 0x01, 0x93, 0x87, 0x47, 0x00, 0x13, 0x08, 0x48, 0x00, 0xe3, 0xe8, 0xd7, 0xfe, 0xb3, 0x85, + 0xe5, 0x00, 0x13, 0x76, 0x36, 0x00, 0x33, 0x87, 0xc6, 0x00, 0x63, 0xea, 0xe6, 0x00, 0x6f, 0x00, 0x80, 0x02, 0x93, + 0x06, 0x05, 0x00, 0x33, 0x07, 0xc5, 0x00, 0x63, 0x7e, 0xe5, 0x00, 0x03, 0xc7, 0x05, 0x00, 0x13, 0x06, 0xf6, 0xff, + 0x23, 0x80, 0xe6, 0x00, 0x93, 0x86, 0x16, 0x00, 0x93, 0x85, 0x15, 0x00, 0xe3, 0x16, 0x06, 0xfe, 0x83, 0x20, 0xc1, + 0x01, 0x03, 0x24, 0x81, 0x01, 0x13, 0x01, 0x01, 0x02, 0x67, 0x80, 0x00, 0x00, 0x13, 0x08, 0x00, 0x00, 0x93, 0x02, + 0x40, 0x00, 0x23, 0x2a, 0x04, 0xfe, 0x33, 0x83, 0x12, 0x41, 0x93, 0x02, 0x44, 0xff, 0x93, 0x73, 0x13, 0x00, 0xb3, + 0xe2, 0x12, 0x01, 0x63, 0x9e, 0x03, 0x04, 0x13, 0x73, 0x23, 0x00, 0x63, 0x14, 0x03, 0x06, 0x83, 0x2e, 0x44, 0xff, + 0x13, 0x98, 0x38, 0x00, 0x93, 0x82, 0x47, 0x00, 0x33, 0x8f, 0x15, 0x41, 0x63, 0xfc, 0xd2, 0x06, 0xb3, 0x02, 0x00, + 0x41, 0x13, 0xfe, 0x82, 0x01, 0x83, 0x22, 0x4f, 0x00, 0x93, 0x03, 0x4f, 0x00, 0xb3, 0xde, 0x0e, 0x01, 0x13, 0x83, + 0x47, 0x00, 0x33, 0x9f, 0xc2, 0x01, 0xb3, 0x6e, 0xdf, 0x01, 0x93, 0x8f, 0x87, 0x00, 0x23, 0xa0, 0xd7, 0x01, 0x93, + 0x07, 0x03, 0x00, 0x13, 0x8f, 0x03, 0x00, 0x93, 0x8e, 0x02, 0x00, 0xe3, 0xea, 0xdf, 0xfc, 0x6f, 0x00, 0x80, 0x04, + 0x03, 0xc8, 0x05, 0x00, 0x23, 0x80, 0x02, 0x01, 0x13, 0x08, 0x10, 0x00, 0x13, 0x73, 0x23, 0x00, 0xe3, 0x00, 0x03, + 0xfa, 0x33, 0x83, 0x05, 0x01, 0x03, 0x13, 0x03, 0x00, 0x33, 0x88, 0x02, 0x01, 0x23, 0x10, 0x68, 0x00, 0x83, 0x2e, + 0x44, 0xff, 0x13, 0x98, 0x38, 0x00, 0x93, 0x82, 0x47, 0x00, 0x33, 0x8f, 0x15, 0x41, 0xe3, 0xe8, 0xd2, 0xf8, 0x93, + 0x82, 0x0e, 0x00, 0x93, 0x03, 0x0f, 0x00, 0x13, 0x83, 0x07, 0x00, 0x23, 0x08, 0x04, 0xfe, 0x93, 0x07, 0x10, 0x00, + 0x23, 0x07, 0x04, 0xfe, 0x63, 0x9c, 0xf8, 0x00, 0x93, 0x08, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, 0x13, 0x0e, 0x00, + 0x00, 0x93, 0x0e, 0x04, 0xff, 0x6f, 0x00, 0xc0, 0x01, 0x83, 0xc8, 0x43, 0x00, 0x83, 0xc7, 0x53, 0x00, 0x13, 0x0e, + 0x20, 0x00, 0x23, 0x08, 0x14, 0xff, 0x93, 0x97, 0x87, 0x00, 0x93, 0x0e, 0xe4, 0xfe, 0x13, 0xff, 0x15, 0x00, 0x63, + 0x16, 0x0f, 0x00, 0x93, 0x03, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x02, 0x93, 0x83, 0x43, 0x00, 0xb3, 0x83, 0xc3, 0x01, + 0x83, 0xc8, 0x03, 0x00, 0x23, 0x80, 0x1e, 0x01, 0x83, 0x43, 0xe4, 0xfe, 0x83, 0x48, 0x04, 0xff, 0x93, 0x93, 0x03, + 0x01, 0xb3, 0xe8, 0x13, 0x01, 0xb3, 0xd2, 0x02, 0x01, 0x33, 0x08, 0x00, 0x41, 0xb3, 0xe7, 0x17, 0x01, 0x13, 0x78, + 0x88, 0x01, 0xb3, 0x97, 0x07, 0x01, 0xb3, 0xe7, 0x57, 0x00, 0x23, 0x20, 0xf3, 0x00, 0xb3, 0x85, 0xe5, 0x00, 0x13, + 0x76, 0x36, 0x00, 0x33, 0x87, 0xc6, 0x00, 0xe3, 0xe4, 0xe6, 0xe8, 0x6f, 0xf0, 0xdf, 0xe9, 0x13, 0x01, 0x01, 0xff, + 0x23, 0x26, 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x13, 0x04, 0x01, 0x01, 0x83, 0x20, 0xc1, 0x00, 0x03, 0x24, 0x81, + 0x00, 0x13, 0x01, 0x01, 0x01, 0x17, 0x03, 0x00, 0x00, 0x67, 0x00, 0x43, 0xdc, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, + 0x11, 0x00, 0x23, 0x24, 0x81, 0x00, 0x13, 0x04, 0x01, 0x01, 0x93, 0x06, 0x00, 0x01, 0x63, 0x62, 0xd6, 0x08, 0xb3, + 0x06, 0xa0, 0x40, 0x93, 0xf6, 0x36, 0x00, 0x33, 0x07, 0xd5, 0x00, 0x63, 0x7e, 0xe5, 0x00, 0x93, 0x87, 0x06, 0x00, + 0x13, 0x08, 0x05, 0x00, 0x23, 0x00, 0xb8, 0x00, 0x93, 0x87, 0xf7, 0xff, 0x13, 0x08, 0x18, 0x00, 0xe3, 0x9a, 0x07, + 0xfe, 0x33, 0x06, 0xd6, 0x40, 0x93, 0x76, 0xc6, 0xff, 0xb3, 0x06, 0xd7, 0x00, 0x63, 0x70, 0xd7, 0x02, 0x93, 0xf7, + 0xf5, 0x0f, 0x37, 0x08, 0x01, 0x01, 0x13, 0x08, 0x18, 0x10, 0xb3, 0x87, 0x07, 0x03, 0x23, 0x20, 0xf7, 0x00, 0x13, + 0x07, 0x47, 0x00, 0xe3, 0x6c, 0xd7, 0xfe, 0x13, 0x76, 0x36, 0x00, 0x33, 0x87, 0xc6, 0x00, 0x63, 0xfa, 0xe6, 0x00, + 0x23, 0x80, 0xb6, 0x00, 0x13, 0x06, 0xf6, 0xff, 0x93, 0x86, 0x16, 0x00, 0xe3, 0x1a, 0x06, 0xfe, 0x83, 0x20, 0xc1, + 0x00, 0x03, 0x24, 0x81, 0x00, 0x13, 0x01, 0x01, 0x01, 0x67, 0x80, 0x00, 0x00, 0x93, 0x06, 0x05, 0x00, 0x33, 0x07, + 0xc5, 0x00, 0xe3, 0x6c, 0xe5, 0xfc, 0x6f, 0xf0, 0x5f, 0xfe, +]; diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/.cargo/config.toml b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/.cargo/config.toml new file mode 100644 index 00000000..7d6e3f00 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/.cargo/config.toml @@ -0,0 +1,9 @@ +[build] +target = "riscv32im-unknown-none-elf" + +[target.riscv32im-unknown-none-elf] +rustflags = ["-C", "link-arg=-T../../../../nightstream-sdk/guest/riscv32im-unknown-none-elf.ld"] + +[env] +# The repo sets RUSTFLAGS globally for host builds. That breaks cross-compiling this guest. +RUSTFLAGS = "" diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/.gitignore b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/Cargo.lock b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/Cargo.lock new file mode 100644 index 00000000..bd7546c5 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/Cargo.lock @@ -0,0 +1,351 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "circuit-l2-transfer" +version = "0.0.0" +dependencies = [ + "nightstream-sdk", + "qp-poseidon-core", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "nightstream-sdk" +version = "0.1.0" +dependencies = [ + "nightstream-sdk-macros", +] + +[[package]] +name = "nightstream-sdk-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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 = "p3-dft" +version = "0.3.0" +dependencies = [ + "itertools", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", +] + +[[package]] +name = "p3-field" +version = "0.3.0" +dependencies = [ + "itertools", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand", + "serde", +] + +[[package]] +name = "p3-goldilocks" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552849f6309ffde34af0d31aa9a2d0a549cb0ec138d9792bfbf4a17800742362" +dependencies = [ + "num-bigint", + "p3-dft", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "p3-util", + "paste", + "rand", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.3.0" +dependencies = [ + "itertools", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand", + "serde", + "transpose", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f765046b763d046728b3246b690f81dfa7ccd7523b7a1582c74f616fbce6a0" + +[[package]] +name = "p3-mds" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c90541c6056712daf2ee69ec328db8b5605ae8dbafe60226c8eb75eaac0e1f9" +dependencies = [ + "p3-dft", + "p3-field", + "p3-symmetric", + "p3-util", + "rand", +] + +[[package]] +name = "p3-poseidon2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e9f053f120a78ad27e9c1991a0ea547777328ca24025c42364d6ee2667d59a" +dependencies = [ + "p3-field", + "p3-mds", + "p3-symmetric", + "p3-util", + "rand", +] + +[[package]] +name = "p3-symmetric" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d5db8f05a26d706dfd8aaf7aa4272ca4f3e7a075db897ec7108f24fad78759" +dependencies = [ + "itertools", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfee67245d9ce78a15176728da2280032f0a84b5819a39a953e7ec03cfd9bd7" +dependencies = [ + "serde", +] + +[[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 = "qp-poseidon-constants" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15005ef7ac88e170a9461990130619855284bd524e8ad059491ed291222d1553" +dependencies = [ + "p3-field", + "p3-goldilocks", + "p3-poseidon2", + "rand", + "rand_chacha", +] + +[[package]] +name = "qp-poseidon-core" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f65766d6de64eff741c7f402002a3322f5e563d53e0e9040aeab4921ff24f2b" +dependencies = [ + "p3-field", + "p3-goldilocks", + "p3-poseidon2", + "p3-symmetric", + "qp-poseidon-constants", + "rand_chacha", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/Cargo.toml b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/Cargo.toml new file mode 100644 index 00000000..bd8c8903 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "circuit-l2-transfer" +version = "0.0.0" +edition = "2021" +publish = false + +[[bin]] +name = "circuit_l2_transfer" +path = "src/main.rs" + +# Standalone guest project: do not inherit the repo workspace. +[workspace] + +[profile.release] +panic = "abort" + +[dependencies] +nightstream-sdk = { path = "../../../../nightstream-sdk", default-features = false } +qp-poseidon-core = { version = "1.0.7", default-features = false, features = ["p3"] } + +[patch.crates-io] +p3-field = { path = "vendor/p3-field" } +p3-matrix = { path = "vendor/p3-matrix" } +p3-dft = { path = "vendor/p3-dft" } diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/README.md b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/README.md new file mode 100644 index 00000000..7227baea --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/README.md @@ -0,0 +1,21 @@ +# `circuit-l2-transfer` guest + +RV32IM guest scaffold for an L2 transfer circuit-like workload. + +Notes: +- The business logic mirrors the requested program shape. +- Poseidon2 here is a temporary in-guest placeholder mixer so the binary can be built today. +- Once the Poseidon2 precompile path is available in `nightstream-sdk` and VM execution, replace + the placeholder with the real precompile call and un-ignore the runtime prove/verify test. + +## Regenerating the committed ROM bytes + +From this directory: + +```bash +python3 export_rom_rs.py +``` + +This updates `crates/neo-fold/riscv-tests/binaries/circuit_l2_transfer_rom.rs`. + +Prereq: `rustup target add riscv32im-unknown-none-elf`. diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/export_rom_rs.py b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/export_rom_rs.py new file mode 100644 index 00000000..a01de9c1 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/export_rom_rs.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import hashlib +import struct +import subprocess +import sys +from pathlib import Path + + +def _read_cstr(buf: bytes, off: int) -> str: + end = buf.find(b"\x00", off) + if end < 0: + raise ValueError("unterminated string in shstrtab") + return buf[off:end].decode("utf-8", errors="strict") + + +def _extract_elf32_section(data: bytes, section_name: str) -> tuple[int, bytes]: + if data[:4] != b"\x7fELF": + raise ValueError("not an ELF file") + + ei_class = data[4] + ei_data = data[5] + if ei_class != 1: + raise ValueError(f"expected ELFCLASS32 (1), got {ei_class}") + if ei_data != 1: + raise ValueError(f"expected little-endian ELF (1), got {ei_data}") + + e_shoff = struct.unpack_from("= e_shnum: + raise ValueError(f"invalid e_shstrndx={e_shstrndx} for e_shnum={e_shnum}") + + def read_shdr(i: int) -> tuple[int, int, int, int, int]: + off = e_shoff + i * e_shentsize + sh_name = struct.unpack_from("= len(shstrtab): + continue + name = _read_cstr(shstrtab, sh_name) + if name != section_name: + continue + if sh_offset + sh_size > len(data): + raise ValueError(f"section {section_name} is out of bounds") + if sh_addralign and (sh_addr % sh_addralign) != 0: + raise ValueError(f"section {section_name} addr alignment mismatch") + return sh_addr, data[sh_offset : sh_offset + sh_size] + + raise ValueError(f"missing section {section_name}") + + +def _format_u8_array(name: str, base: int, content: bytes, sha256_hex: str) -> str: + lines = [] + lines.append("// @generated by crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/export_rom_rs.py") + lines.append(f"// sha256={sha256_hex}") + lines.append("") + lines.append(f"pub const {name}_BASE: u64 = {base}u64;") + lines.append(f"pub const {name}: [u8; {len(content)}] = [") + for i in range(0, len(content), 64): + chunk = content[i : i + 64] + hexes = ", ".join(f"0x{b:02x}" for b in chunk) + lines.append(f" {hexes},") + lines.append("];") + lines.append("") + return "\n".join(lines) + + +def _find_repo_root(start: Path) -> Path: + for cand in [start, *start.parents]: + if (cand / "Cargo.toml").is_file() and (cand / "crates").is_dir(): + return cand + raise FileNotFoundError(f"failed to find repo root from {start}") + + +def main() -> int: + guest_dir = Path(__file__).resolve().parent + repo_root = _find_repo_root(guest_dir) + out_rs = repo_root / "crates/neo-fold/riscv-tests/binaries/circuit_l2_transfer_rom.rs" + + subprocess.run(["cargo", "build", "--release"], cwd=guest_dir, check=True) + + elf = guest_dir / "target/riscv32im-unknown-none-elf/release/circuit_l2_transfer" + if not elf.exists(): + raise FileNotFoundError(f"missing expected ELF: {elf}") + + data = elf.read_bytes() + base, text = _extract_elf32_section(data, ".neo_start") + if len(text) == 0: + raise ValueError(".neo_start is empty") + if len(text) % 4 != 0: + raise ValueError(f".neo_start length must be multiple of 4, got {len(text)}") + + sha256_hex = hashlib.sha256(text).hexdigest() + out_rs.parent.mkdir(parents=True, exist_ok=True) + out_rs.write_text(_format_u8_array("CIRCUIT_L2_TRANSFER_ROM", base, text, sha256_hex), encoding="utf-8") + + rel = out_rs.relative_to(repo_root) + print(f"Wrote {rel} (len={len(text)} sha256={sha256_hex})") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/src/main.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/src/main.rs new file mode 100644 index 00000000..5dde0688 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/src/main.rs @@ -0,0 +1,586 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::vec::Vec; + +type GlDigest = [u64; 4]; + +const GL_ZERO: u64 = 0; +const GL_ONE: u64 = 1; +const ZERO_DIGEST: GlDigest = [0, 0, 0, 0]; + +struct GuestBumpAlloc; + +unsafe impl core::alloc::GlobalAlloc for GuestBumpAlloc { + #[allow(static_mut_refs)] + unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { + static mut HEAP: [u8; 128 * 1024] = [0; 128 * 1024]; + static mut OFFSET: usize = 0; + + let align = layout.align(); + let size = layout.size(); + let mut off = OFFSET; + off = (off + align - 1) & !(align - 1); + if off.checked_add(size).is_none() || off + size > HEAP.len() { + return core::ptr::null_mut(); + } + OFFSET = off + size; + HEAP.as_mut_ptr().add(off) + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: core::alloc::Layout) {} +} + +#[global_allocator] +static GUEST_ALLOC: GuestBumpAlloc = GuestBumpAlloc; + +#[inline] +fn gl_add(a: u64, b: u64) -> u64 { + a.wrapping_add(b) +} + +#[inline] +fn gl_sub(a: u64, b: u64) -> u64 { + a.wrapping_sub(b) +} + +#[inline] +fn gl_mul(a: u64, b: u64) -> u64 { + a.wrapping_mul(b) +} + +#[inline] +fn digest_eq(a: &GlDigest, b: &GlDigest) -> bool { + a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] +} + +#[inline] +fn poseidon2_hash(input: &[u64]) -> GlDigest { + let mut bytes = Vec::with_capacity(input.len() * 8); + for &x in input { + bytes.extend_from_slice(&x.to_le_bytes()); + } + + let digest = qp_poseidon_core::hash_variable_length_bytes(&bytes); + let mut out = [0u64; 4]; + for (i, chunk) in digest.chunks_exact(8).take(4).enumerate() { + let mut limb = [0u8; 8]; + limb.copy_from_slice(chunk); + out[i] = u64::from_le_bytes(limb); + } + out +} + +const MAX_INS: usize = 4; +const MAX_OUTS: usize = 2; + +const TAG_MT_NODE: u64 = 1; +const TAG_NOTE: u64 = 2; +const TAG_PRF_NF: u64 = 3; +const TAG_PK: u64 = 4; +const TAG_ADDR: u64 = 5; +const TAG_NFKEY: u64 = 6; +const TAG_BL_BUCKET: u64 = 7; + +const BL_DEPTH: u32 = 16; +const BL_BUCKET_SIZE: usize = 12; + +const INPUT_ADDR: u32 = 0x104; +const OUTPUT_ADDR: u32 = 0x100; + +struct RamReader { + addr: u32, +} + +impl RamReader { + fn new(addr: u32) -> Self { + Self { addr } + } + + fn read_u32(&mut self) -> u32 { + let val = unsafe { core::ptr::read_volatile(self.addr as *const u32) }; + self.addr += 4; + val + } + + fn read_u64(&mut self) -> u64 { + let lo = self.read_u32() as u64; + let hi = self.read_u32() as u64; + lo | (hi << 32) + } + + fn read_digest(&mut self) -> GlDigest { + [self.read_u64(), self.read_u64(), self.read_u64(), self.read_u64()] + } +} + +struct RamWriter { + addr: u32, +} + +impl RamWriter { + fn new(addr: u32) -> Self { + Self { addr } + } + + fn write_u32(&mut self, val: u32) { + unsafe { core::ptr::write_volatile(self.addr as *mut u32, val) }; + self.addr += 4; + } + + fn write_u64(&mut self, val: u64) { + self.write_u32(val as u32); + self.write_u32((val >> 32) as u32); + } + + fn write_digest(&mut self, d: &GlDigest) { + for &elem in d { + self.write_u64(elem); + } + } +} + +fn derive_pk_spend(spend_sk: &GlDigest) -> GlDigest { + let mut input = [0u64; 5]; + input[0] = TAG_PK; + input[1..5].copy_from_slice(spend_sk); + poseidon2_hash(&input) +} + +fn derive_nf_key(domain: &GlDigest, spend_sk: &GlDigest) -> GlDigest { + let mut input = [0u64; 9]; + input[0] = TAG_NFKEY; + input[1..5].copy_from_slice(domain); + input[5..9].copy_from_slice(spend_sk); + poseidon2_hash(&input) +} + +fn derive_address(domain: &GlDigest, pk_spend: &GlDigest, pk_ivk: &GlDigest) -> GlDigest { + let mut input = [0u64; 13]; + input[0] = TAG_ADDR; + input[1..5].copy_from_slice(domain); + input[5..9].copy_from_slice(pk_spend); + input[9..13].copy_from_slice(pk_ivk); + poseidon2_hash(&input) +} + +fn note_commitment( + domain: &GlDigest, + value: u64, + rho: &GlDigest, + recipient: &GlDigest, + sender_id: &GlDigest, +) -> GlDigest { + let mut input = [0u64; 18]; + input[0] = TAG_NOTE; + input[1..5].copy_from_slice(domain); + input[5] = value; + input[6..10].copy_from_slice(rho); + input[10..14].copy_from_slice(recipient); + input[14..18].copy_from_slice(sender_id); + poseidon2_hash(&input) +} + +fn derive_nullifier(domain: &GlDigest, nf_key: &GlDigest, rho: &GlDigest) -> GlDigest { + let mut input = [0u64; 13]; + input[0] = TAG_PRF_NF; + input[1..5].copy_from_slice(domain); + input[5..9].copy_from_slice(nf_key); + input[9..13].copy_from_slice(rho); + poseidon2_hash(&input) +} + +fn mt_node(level: u64, left: &GlDigest, right: &GlDigest) -> GlDigest { + let mut input = [0u64; 10]; + input[0] = TAG_MT_NODE; + input[1] = level; + input[2..6].copy_from_slice(left); + input[6..10].copy_from_slice(right); + poseidon2_hash(&input) +} + +fn merkle_root(leaf: &GlDigest, pos: u32, reader: &mut RamReader, depth: u32) -> GlDigest { + let mut cur = *leaf; + let mut p = pos; + + let mut lvl = 0u32; + while lvl < depth { + let sib = reader.read_digest(); + let bit = (p & 1) as u64; + + let mut left = [0u64; 4]; + let mut right = [0u64; 4]; + let mut i = 0usize; + while i < 4 { + let delta = gl_mul(bit, gl_sub(sib[i], cur[i])); + left[i] = gl_add(cur[i], delta); + right[i] = gl_sub(sib[i], delta); + i += 1; + } + + cur = mt_node(lvl as u64, &left, &right); + p >>= 1; + lvl += 1; + } + + cur +} + +fn enforce_prod_digest_diff(acc: u64, a: &GlDigest, b: &GlDigest) -> u64 { + let mut result = acc; + let mut i = 0usize; + while i < 4 { + let diff = gl_sub(a[i], b[i]); + result = gl_mul(result, diff); + i += 1; + } + result +} + +fn bl_bucket_leaf(entries: &[GlDigest; BL_BUCKET_SIZE]) -> GlDigest { + let mut input = [0u64; 1 + BL_BUCKET_SIZE * 4]; + input[0] = TAG_BL_BUCKET; + for (i, entry) in entries.iter().enumerate() { + let off = 1 + i * 4; + input[off..off + 4].copy_from_slice(entry); + } + poseidon2_hash(&input) +} + +fn bl_bucket_pos(id: &GlDigest) -> u32 { + (id[0] as u32) & ((1u32 << BL_DEPTH) - 1) +} + +fn assert_not_blacklisted(id: &GlDigest, blacklist_root: &GlDigest, reader: &mut RamReader) { + let mut entries = [ZERO_DIGEST; BL_BUCKET_SIZE]; + for e in entries.iter_mut() { + *e = reader.read_digest(); + } + let bucket_inv = reader.read_u64(); + + let mut prod: u64 = GL_ONE; + for entry in &entries { + prod = enforce_prod_digest_diff(prod, id, entry); + } + assert!(gl_mul(prod, bucket_inv) == GL_ONE); + + let leaf = bl_bucket_leaf(&entries); + let pos = bl_bucket_pos(id); + let root = merkle_root(&leaf, pos, reader, BL_DEPTH); + assert!(digest_eq(&root, blacklist_root)); +} + +const NOTE_PLAIN_LEN: usize = 272; + +fn digest_to_bytes(d: &GlDigest) -> [u8; 32] { + let mut out = [0u8; 32]; + let mut i = 0usize; + while i < 4 { + let b = d[i].to_le_bytes(); + out[i * 8..(i + 1) * 8].copy_from_slice(&b); + i += 1; + } + out +} + +fn u64_to_le_bytes(v: u64) -> [u8; 8] { + v.to_le_bytes() +} + +fn poseidon2_hash_packed_bytes(bytes: &[u8], len: usize) -> GlDigest { + let n_elems = (len + 7) / 8; + let mut felts = [0u64; 64]; + let mut i = 0usize; + while i < n_elems { + let off = i * 8; + let mut buf = [0u8; 8]; + let take = if off + 8 <= len { 8 } else { len - off }; + let mut j = 0usize; + while j < take { + buf[j] = bytes[off + j]; + j += 1; + } + felts[i] = u64::from_le_bytes(buf); + i += 1; + } + felts[n_elems] = len as u64; + poseidon2_hash(&felts[..n_elems + 1]) +} + +fn view_fvk_commitment(fvk: &GlDigest) -> GlDigest { + let mut buf = [0u8; 13 + 32]; + buf[..13].copy_from_slice(b"FVK_COMMIT_V1"); + buf[13..45].copy_from_slice(&digest_to_bytes(fvk)); + poseidon2_hash_packed_bytes(&buf, 45) +} + +fn view_kdf(fvk: &GlDigest, cm: &GlDigest) -> GlDigest { + let mut buf = [0u8; 11 + 32 + 32]; + buf[..11].copy_from_slice(b"VIEW_KDF_V1"); + buf[11..43].copy_from_slice(&digest_to_bytes(fvk)); + buf[43..75].copy_from_slice(&digest_to_bytes(cm)); + poseidon2_hash_packed_bytes(&buf, 75) +} + +fn view_stream_block(k: &GlDigest, ctr: u32) -> GlDigest { + let mut buf = [0u8; 14 + 32 + 4]; + buf[..14].copy_from_slice(b"VIEW_STREAM_V1"); + buf[14..46].copy_from_slice(&digest_to_bytes(k)); + buf[46..50].copy_from_slice(&ctr.to_le_bytes()); + poseidon2_hash_packed_bytes(&buf, 50) +} + +fn view_ct_hash(ct: &[u8; NOTE_PLAIN_LEN]) -> GlDigest { + let mut buf = [0u8; 10 + NOTE_PLAIN_LEN]; + buf[..10].copy_from_slice(b"CT_HASH_V1"); + buf[10..10 + NOTE_PLAIN_LEN].copy_from_slice(ct); + poseidon2_hash_packed_bytes(&buf, 10 + NOTE_PLAIN_LEN) +} + +fn view_mac(k: &GlDigest, cm: &GlDigest, ct_h: &GlDigest) -> GlDigest { + let mut buf = [0u8; 11 + 32 + 32 + 32]; + buf[..11].copy_from_slice(b"VIEW_MAC_V1"); + buf[11..43].copy_from_slice(&digest_to_bytes(k)); + buf[43..75].copy_from_slice(&digest_to_bytes(cm)); + buf[75..107].copy_from_slice(&digest_to_bytes(ct_h)); + poseidon2_hash_packed_bytes(&buf, 107) +} + +fn view_stream_xor_encrypt(k: &GlDigest, pt: &[u8; NOTE_PLAIN_LEN]) -> [u8; NOTE_PLAIN_LEN] { + let mut ct = [0u8; NOTE_PLAIN_LEN]; + let mut ctr: u32 = 0; + let mut off: usize = 0; + while off < NOTE_PLAIN_LEN { + let ks = view_stream_block(k, ctr); + let ks_bytes = digest_to_bytes(&ks); + ctr += 1; + let take = if off + 32 <= NOTE_PLAIN_LEN { + 32 + } else { + NOTE_PLAIN_LEN - off + }; + let mut j = 0usize; + while j < take { + ct[off + j] = pt[off + j] ^ ks_bytes[j]; + j += 1; + } + off += take; + } + ct +} + +fn encode_note_plain( + domain: &GlDigest, + value: u64, + rho: &GlDigest, + recipient: &GlDigest, + sender_id: &GlDigest, + cm_ins: &[GlDigest; MAX_INS], + n_in: u32, +) -> [u8; NOTE_PLAIN_LEN] { + let mut pt = [0u8; NOTE_PLAIN_LEN]; + let dom = digest_to_bytes(domain); + pt[..32].copy_from_slice(&dom); + pt[32..40].copy_from_slice(&u64_to_le_bytes(value)); + pt[48..80].copy_from_slice(&digest_to_bytes(rho)); + pt[80..112].copy_from_slice(&digest_to_bytes(recipient)); + pt[112..144].copy_from_slice(&digest_to_bytes(sender_id)); + let mut i = 0usize; + while i < MAX_INS { + let off = 144 + i * 32; + if (i as u32) < n_in { + pt[off..off + 32].copy_from_slice(&digest_to_bytes(&cm_ins[i])); + } + i += 1; + } + pt +} + +#[nightstream_sdk::provable] +fn note_spend() -> ! { + let mut r = RamReader::new(INPUT_ADDR); + let mut w = RamWriter::new(OUTPUT_ADDR); + + let domain = r.read_digest(); + let spend_sk = r.read_digest(); + let pk_ivk_owner = r.read_digest(); + let depth = r.read_u32(); + let anchor = r.read_digest(); + let n_in = r.read_u32(); + + assert!(n_in <= MAX_INS as u32); + + let pk_spend_owner = derive_pk_spend(&spend_sk); + let nf_key = derive_nf_key(&domain, &spend_sk); + let recipient_owner = derive_address(&domain, &pk_spend_owner, &pk_ivk_owner); + let sender_id = recipient_owner; + + let mut sum_in: u64 = GL_ZERO; + let mut enforce_prod: u64 = GL_ONE; + let mut input_rhos: [GlDigest; MAX_INS] = [ZERO_DIGEST; MAX_INS]; + let mut input_cms: [GlDigest; MAX_INS] = [ZERO_DIGEST; MAX_INS]; + + for i in 0..n_in as usize { + let value_in = r.read_u64(); + let rho_in = r.read_digest(); + let sender_id_in = r.read_digest(); + let pos = r.read_u32(); + + sum_in = gl_add(sum_in, value_in); + enforce_prod = gl_mul(enforce_prod, value_in); + + let cm = note_commitment(&domain, value_in, &rho_in, &recipient_owner, &sender_id_in); + input_cms[i] = cm; + input_rhos[i] = rho_in; + + let root = merkle_root(&cm, pos, &mut r, depth); + assert!(digest_eq(&root, &anchor)); + } + + let mut nullifiers: [GlDigest; MAX_INS] = [ZERO_DIGEST; MAX_INS]; + for i in 0..n_in as usize { + let nullifier_pub = r.read_digest(); + let nf = derive_nullifier(&domain, &nf_key, &input_rhos[i]); + assert!(digest_eq(&nf, &nullifier_pub)); + nullifiers[i] = nullifier_pub; + } + + for i in 0..n_in as usize { + for j in (i + 1)..n_in as usize { + assert!(!digest_eq(&nullifiers[i], &nullifiers[j])); + } + } + + let withdraw_amount = r.read_u64(); + let withdraw_to = r.read_digest(); + let n_out = r.read_u32(); + + assert!(n_out <= MAX_OUTS as u32); + + if withdraw_amount == 0 { + assert!(n_out >= 1); + assert!(digest_eq(&withdraw_to, &ZERO_DIGEST)); + } else { + assert!(n_out <= 1); + assert!(!digest_eq(&withdraw_to, &ZERO_DIGEST)); + } + + let mut out_sum: u64 = GL_ZERO; + let mut output_values: [u64; MAX_OUTS] = [0; MAX_OUTS]; + let mut output_rhos: [GlDigest; MAX_OUTS] = [ZERO_DIGEST; MAX_OUTS]; + let mut output_cms: [GlDigest; MAX_OUTS] = [ZERO_DIGEST; MAX_OUTS]; + let mut output_rcps: [GlDigest; MAX_OUTS] = [ZERO_DIGEST; MAX_OUTS]; + + for j in 0..n_out as usize { + let value_out = r.read_u64(); + output_values[j] = value_out; + let rho_out = r.read_digest(); + let pk_spend_out = r.read_digest(); + let pk_ivk_out = r.read_digest(); + + out_sum = gl_add(out_sum, value_out); + enforce_prod = gl_mul(enforce_prod, value_out); + + let rcp = derive_address(&domain, &pk_spend_out, &pk_ivk_out); + let cm = note_commitment(&domain, value_out, &rho_out, &rcp, &sender_id); + + output_rhos[j] = rho_out; + output_cms[j] = cm; + output_rcps[j] = rcp; + } + + let mut cm_outs_pub: [GlDigest; MAX_OUTS] = [ZERO_DIGEST; MAX_OUTS]; + for j in 0..n_out as usize { + let cm_pub = r.read_digest(); + assert!(digest_eq(&output_cms[j], &cm_pub)); + cm_outs_pub[j] = cm_pub; + } + + let rhs = gl_add(withdraw_amount, out_sum); + assert!(sum_in == rhs); + + if withdraw_amount > 0 && n_out == 1 { + assert!(digest_eq(&output_rcps[0], &sender_id)); + } + if withdraw_amount == 0 && n_out == 2 { + assert!(digest_eq(&output_rcps[1], &sender_id)); + } + + for j in 0..n_out as usize { + for i in 0..n_in as usize { + enforce_prod = enforce_prod_digest_diff(enforce_prod, &output_rhos[j], &input_rhos[i]); + } + } + if n_out == 2 { + enforce_prod = enforce_prod_digest_diff(enforce_prod, &output_rhos[0], &output_rhos[1]); + } + + let inv_enforce = r.read_u64(); + let check = gl_mul(enforce_prod, inv_enforce); + assert!(check == GL_ONE); + + let blacklist_root = r.read_digest(); + assert_not_blacklisted(&sender_id, &blacklist_root, &mut r); + + if withdraw_amount == 0 { + assert_not_blacklisted(&output_rcps[0], &blacklist_root, &mut r); + } + + let n_viewers = r.read_u32(); + + w.write_digest(&anchor); + w.write_u32(n_in); + for i in 0..n_in as usize { + w.write_digest(&nullifiers[i]); + } + w.write_u64(withdraw_amount); + w.write_digest(&withdraw_to); + w.write_u32(n_out); + for j in 0..n_out as usize { + w.write_digest(&cm_outs_pub[j]); + } + w.write_digest(&blacklist_root); + + w.write_u32(n_viewers); + + for _v in 0..n_viewers as usize { + let fvk_commitment_pub = r.read_digest(); + let fvk = r.read_digest(); + + let computed_fvk_cm = view_fvk_commitment(&fvk); + assert!(digest_eq(&computed_fvk_cm, &fvk_commitment_pub)); + + for j in 0..n_out as usize { + let ct_hash_pub = r.read_digest(); + let mac_pub = r.read_digest(); + + let k = view_kdf(&fvk, &output_cms[j]); + + let pt = encode_note_plain( + &domain, + output_values[j], + &output_rhos[j], + &output_rcps[j], + &sender_id, + &input_cms, + n_in, + ); + + let ct = view_stream_xor_encrypt(&k, &pt); + let ct_h = view_ct_hash(&ct); + assert!(digest_eq(&ct_h, &ct_hash_pub)); + + let mac = view_mac(&k, &output_cms[j], &ct_h); + assert!(digest_eq(&mac, &mac_pub)); + + w.write_digest(&output_cms[j]); + w.write_digest(&fvk_commitment_pub); + w.write_digest(&ct_hash_pub); + w.write_digest(&mac_pub); + } + } + + nightstream_sdk::halt(); +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/.cargo-ok b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/.cargo-ok new file mode 100644 index 00000000..5f8b7958 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/.cargo_vcs_info.json b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/.cargo_vcs_info.json new file mode 100644 index 00000000..b87698b4 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/.cargo_vcs_info.json @@ -0,0 +1,7 @@ +{ + "git": { + "sha1": "3148b7148a19bb56dedb55291ae9b06223904e88", + "dirty": true + }, + "path_in_vcs": "dft" +} \ No newline at end of file diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.lock b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.lock new file mode 100644 index 00000000..20499eb4 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.lock @@ -0,0 +1,735 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "criterion" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[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.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "p3-dft" +version = "0.3.0" +dependencies = [ + "criterion", + "itertools 0.14.0", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "rand", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc13a73509fe09c67b339951ca8d4cc6e61c9bf08c130dbc90dda52452918cc2" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand", + "serde", + "tracing", +] + +[[package]] +name = "p3-matrix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e1e9f69c2fe15768b3ceb2915edb88c47398aa22c485d8163deab2a47fe194" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand", + "serde", + "tracing", + "transpose", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f765046b763d046728b3246b690f81dfa7ccd7523b7a1582c74f616fbce6a0" +dependencies = [ + "rayon", +] + +[[package]] +name = "p3-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfee67245d9ce78a15176728da2280032f0a84b5819a39a953e7ec03cfd9bd7" +dependencies = [ + "serde", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.toml b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.toml new file mode 100644 index 00000000..8fed2e6d --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.toml @@ -0,0 +1,75 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +name = "p3-dft" +version = "0.3.0" +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Plonky3 is a toolkit for implementing polynomial IOPs (PIOPs), such as PLONK and STARKs." +homepage = "https://github.com/0xPolygonZero/Plonky3" +readme = false +keywords = [ + "cryptography", + "SNARK", + "PLONK", + "FRI", + "plonky3", +] +categories = ["cryptography::cryptocurrencies"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/0xPolygonZero/Plonky3" +resolver = "2" + +[features] +nightly-features = [] +parallel = ["p3-maybe-rayon/parallel"] + +[lib] +name = "p3_dft" +path = "src/lib.rs" + +[[bench]] +name = "fft" +path = "benches/fft.rs" +harness = false + +[dependencies.itertools] +version = "0.14.0" +features = ["use_alloc"] +default-features = false + +[dependencies.p3-field] +version = "0.3.0" + +[dependencies.p3-matrix] +version = "0.3.0" + +[dependencies.p3-maybe-rayon] +version = "0.3.0" + +[dependencies.p3-util] +version = "0.3.0" +["attributes"] +default-features = false + +[dev-dependencies.criterion] +version = "0.6" + +[dev-dependencies.rand] +version = "0.9.0" +features = ["small_rng"] +default-features = false diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.toml.orig b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.toml.orig new file mode 100644 index 00000000..462e3cf1 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/Cargo.toml.orig @@ -0,0 +1,42 @@ +[package] +name = "p3-dft" +# TODO: Replace this generic plonky3 description with one specific to this crate... +description.workspace = true +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +keywords.workspace = true +categories.workspace = true + +[dependencies] +p3-field.workspace = true +p3-matrix.workspace = true +p3-maybe-rayon.workspace = true +p3-util.workspace = true + +itertools.workspace = true +tracing.workspace = true + +[dev-dependencies] +p3-baby-bear = { path = "../baby-bear" } +p3-goldilocks = { path = "../goldilocks" } +p3-mersenne-31 = { path = "../mersenne-31" } +p3-monty-31 = { path = "../monty-31" } + +criterion.workspace = true +rand.workspace = true + +[features] +nightly-features = [ + "p3-goldilocks/nightly-features", + "p3-monty-31/nightly-features", + "p3-baby-bear/nightly-features", + "p3-mersenne-31/nightly-features", +] +parallel = ["p3-maybe-rayon/parallel"] + +[[bench]] +name = "fft" +harness = false diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/benches/fft.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/benches/fft.rs new file mode 100644 index 00000000..7b466cd2 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/benches/fft.rs @@ -0,0 +1,205 @@ +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; +use p3_baby_bear::BabyBear; +use p3_dft::{Radix2Bowers, Radix2DFTSmallBatch, Radix2Dit, Radix2DitParallel, TwoAdicSubgroupDft}; +use p3_field::extension::{BinomialExtensionField, Complex}; +use p3_field::{Algebra, BasedVectorSpace, TwoAdicField}; +use p3_goldilocks::Goldilocks; +use p3_matrix::dense::RowMajorMatrix; +use p3_mersenne_31::{Mersenne31, Mersenne31ComplexRadix2Dit, Mersenne31Dft}; +use p3_monty_31::dft::RecursiveDft; +use p3_util::pretty_name; +use rand::SeedableRng; +use rand::distr::{Distribution, StandardUniform}; +use rand::rngs::SmallRng; + +fn bench_fft(c: &mut Criterion) { + // log_sizes correspond to the sizes of DFT we want to benchmark; + // for the DFT over the quadratic extension "Mersenne31Complex" a + // fairer comparison is to use half sizes, which is the log minus 1. + let log_sizes = &[14, 16, 18, 20, 22]; + let log_half_sizes = &[13, 15, 17]; + + const BATCH_SIZE: usize = 256; + type BBExt = BinomialExtensionField; + + fft::, BATCH_SIZE>(c, log_sizes); + fft::, BATCH_SIZE>(c, log_sizes); + fft::, BATCH_SIZE>(c, log_sizes); + fft::(c, log_sizes); + fft::, BATCH_SIZE>(c, log_sizes); + fft::, BATCH_SIZE>(c, log_sizes); + fft::(c, log_sizes); + fft::, BATCH_SIZE>(c, log_sizes); + fft::, Radix2Dit<_>, BATCH_SIZE>(c, log_half_sizes); + fft::, Radix2Bowers, BATCH_SIZE>(c, log_half_sizes); + fft::, Radix2DitParallel<_>, BATCH_SIZE>(c, log_half_sizes); + + fft::, Mersenne31ComplexRadix2Dit, BATCH_SIZE>(c, log_half_sizes); + m31_fft::, BATCH_SIZE>(c, log_sizes); + m31_fft::(c, log_sizes); + + ifft::, BATCH_SIZE>(c, log_sizes); + + coset_lde::, BATCH_SIZE>(c, log_sizes); + coset_lde::, BATCH_SIZE>(c, log_sizes); + coset_lde::(c, log_sizes); + coset_lde::, BATCH_SIZE>(c, log_sizes); + coset_lde::(c, log_sizes); + + // The FFT is much slower when handling extension fields so we use smaller sizes: + let ext_log_sizes = &[10, 12, 14]; + const EXT_BATCH_SIZE: usize = 50; + fft::, EXT_BATCH_SIZE>(c, ext_log_sizes); + fft::, EXT_BATCH_SIZE>(c, ext_log_sizes); + fft_algebra::, EXT_BATCH_SIZE>(c, ext_log_sizes); + fft_algebra::, EXT_BATCH_SIZE>(c, ext_log_sizes); + fft_algebra::, EXT_BATCH_SIZE>(c, ext_log_sizes); + fft_algebra::, EXT_BATCH_SIZE>(c, ext_log_sizes); +} + +fn fft(c: &mut Criterion, log_sizes: &[usize]) +where + F: TwoAdicField, + Dft: TwoAdicSubgroupDft, + StandardUniform: Distribution, +{ + let mut group = c.benchmark_group(format!( + "fft/{}/{}/ncols={}", + pretty_name::(), + pretty_name::(), + BATCH_SIZE + )); + group.sample_size(10); + + let mut rng = SmallRng::seed_from_u64(1); + for n_log in log_sizes { + let n = 1 << n_log; + + let messages = RowMajorMatrix::rand(&mut rng, n, BATCH_SIZE); + + let dft = Dft::default(); + group.bench_with_input(BenchmarkId::from_parameter(n), &dft, |b, dft| { + b.iter(|| { + dft.dft_batch(messages.clone()); + }); + }); + } +} + +fn fft_algebra(c: &mut Criterion, log_sizes: &[usize]) +where + F: TwoAdicField, + V: Algebra + BasedVectorSpace + Clone + Default + Send + Sync, + Dft: TwoAdicSubgroupDft, + StandardUniform: Distribution, +{ + let mut group = c.benchmark_group(format!( + "fft_algebra/{}/{}/{}/ncols={}", + pretty_name::(), + pretty_name::(), + pretty_name::(), + BATCH_SIZE + )); + group.sample_size(10); + + let mut rng = SmallRng::seed_from_u64(1); + for n_log in log_sizes { + let n = 1 << n_log; + + let messages = RowMajorMatrix::::rand(&mut rng, n, BATCH_SIZE); + + let dft = Dft::default(); + group.bench_with_input(BenchmarkId::from_parameter(n), &dft, |b, dft| { + b.iter(|| { + dft.dft_algebra_batch(messages.clone()); + }); + }); + } +} + +fn m31_fft(c: &mut Criterion, log_sizes: &[usize]) +where + Dft: TwoAdicSubgroupDft>, + StandardUniform: Distribution, +{ + let mut group = c.benchmark_group(format!( + "m31_fft::<{}, {}>", + pretty_name::(), + BATCH_SIZE + )); + group.sample_size(10); + + let mut rng = SmallRng::seed_from_u64(1); + for n_log in log_sizes { + let n = 1 << n_log; + + let messages = RowMajorMatrix::rand(&mut rng, n, BATCH_SIZE); + + group.bench_function(BenchmarkId::from_parameter(n), |b| { + b.iter(|| { + Mersenne31Dft::dft_batch::(messages.clone()); + }); + }); + } +} + +fn ifft(c: &mut Criterion, log_sizes: &[usize]) +where + F: TwoAdicField, + Dft: TwoAdicSubgroupDft, + StandardUniform: Distribution, +{ + let mut group = c.benchmark_group(format!( + "ifft/{}/{}/ncols={}", + pretty_name::(), + pretty_name::(), + BATCH_SIZE + )); + group.sample_size(10); + + let mut rng = SmallRng::seed_from_u64(1); + for n_log in log_sizes { + let n = 1 << n_log; + + let messages = RowMajorMatrix::rand(&mut rng, n, BATCH_SIZE); + + let dft = Dft::default(); + group.bench_with_input(BenchmarkId::from_parameter(n), &dft, |b, dft| { + b.iter(|| { + dft.idft_batch(messages.clone()); + }); + }); + } +} + +fn coset_lde(c: &mut Criterion, log_sizes: &[usize]) +where + F: TwoAdicField, + Dft: TwoAdicSubgroupDft, + StandardUniform: Distribution, +{ + let mut group = c.benchmark_group(format!( + "coset_lde/{}/{}/ncols={}", + pretty_name::(), + pretty_name::(), + BATCH_SIZE + )); + group.sample_size(10); + + let mut rng = SmallRng::seed_from_u64(1); + for n_log in log_sizes { + let n = 1 << n_log; + + let messages = RowMajorMatrix::rand(&mut rng, n, BATCH_SIZE); + + let dft = Dft::default(); + group.bench_with_input(BenchmarkId::from_parameter(n), &dft, |b, dft| { + b.iter(|| { + dft.coset_lde_batch(messages.clone(), 1, F::GENERATOR); + }); + }); + } +} + +criterion_group!(benches, bench_fft); +criterion_main!(benches); diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/butterflies.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/butterflies.rs new file mode 100644 index 00000000..4097f2ae --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/butterflies.rs @@ -0,0 +1,203 @@ +use core::mem::MaybeUninit; + +use itertools::izip; +use p3_field::{Field, PackedField, PackedValue}; + +/// A butterfly operation used in NTT to combine two values into a new pair. +/// +/// This trait defines how to transform two elements (or vectors of elements) +/// according to the structure of a butterfly gate. +/// +/// In an NTT, butterflies are the core units that recursively combine values +/// across layers. Each butterfly computes: +/// ```text +/// (a + b * twiddle, a - b * twiddle) // DIT +/// or +/// (a + b, (a - b) * twiddle) // DIF +/// ``` +/// The transformation can be applied: +/// - in-place (mutating input values) +/// - to full rows of values (arrays of field elements) +/// - out-of-place (writing results to separate destination buffers) +/// +/// Different butterfly variants (DIT, DIF, or twiddle-free) define the exact formula. +pub trait Butterfly: Copy + Send + Sync { + /// Applies the butterfly transformation to two packed field values. + /// + /// This method takes two inputs `x_1` and `x_2` and returns two outputs `(y_1, y_2)` + /// depending on the butterfly type. + /// ```text + /// Example (DIF): + /// Input: x_1 = a, x_2 = b + /// Output: (a + b, (a - b) * twiddle) + /// ``` + fn apply>(&self, x_1: PF, x_2: PF) -> (PF, PF); + + /// Applies the butterfly in-place to two packed values. + /// + /// Mutates both `x_1` and `x_2` directly, storing the result of `apply`. + #[inline] + fn apply_in_place>(&self, x_1: &mut PF, x_2: &mut PF) { + (*x_1, *x_2) = self.apply(*x_1, *x_2); + } + + /// Applies the butterfly transformation to two rows of scalar field values. + /// + /// Each row is a slice of `F`. This function processes the rows in packed + /// chunks using SIMD where possible, and falls back to scalar operations + /// for the suffix (remaining elements). + /// + /// The transformation is done in-place. + #[inline] + fn apply_to_rows(&self, row_1: &mut [F], row_2: &mut [F]) { + let (shorts_1, suffix_1) = F::Packing::pack_slice_with_suffix_mut(row_1); + let (shorts_2, suffix_2) = F::Packing::pack_slice_with_suffix_mut(row_2); + debug_assert_eq!(shorts_1.len(), shorts_2.len()); + debug_assert_eq!(suffix_1.len(), suffix_2.len()); + for (x_1, x_2) in shorts_1.iter_mut().zip(shorts_2) { + self.apply_in_place(x_1, x_2); + } + for (x_1, x_2) in suffix_1.iter_mut().zip(suffix_2) { + self.apply_in_place(x_1, x_2); + } + } + + /// Applies the butterfly out-of-place to two source rows. + /// + /// This version does not overwrite the source. Instead, it writes the + /// result of each butterfly to separate destination slices (which may + /// be uninitialized memory). + /// + /// This is useful when performing LDE's where the size of the output is larger than the size of the input. + /// + /// - `src_1`, `src_2`: input slices + /// - `dst_1`, `dst_2`: output slices to write to (must be MaybeUninit) + #[inline] + fn apply_to_rows_oop( + &self, + src_1: &[F], + dst_1: &mut [MaybeUninit], + src_2: &[F], + dst_2: &mut [MaybeUninit], + ) { + let (src_shorts_1, src_suffix_1) = F::Packing::pack_slice_with_suffix(src_1); + let (src_shorts_2, src_suffix_2) = F::Packing::pack_slice_with_suffix(src_2); + let (dst_shorts_1, dst_suffix_1) = + F::Packing::pack_maybe_uninit_slice_with_suffix_mut(dst_1); + let (dst_shorts_2, dst_suffix_2) = + F::Packing::pack_maybe_uninit_slice_with_suffix_mut(dst_2); + debug_assert_eq!(src_shorts_1.len(), src_shorts_2.len()); + debug_assert_eq!(src_suffix_1.len(), src_suffix_2.len()); + debug_assert_eq!(dst_shorts_1.len(), dst_shorts_2.len()); + debug_assert_eq!(dst_suffix_1.len(), dst_suffix_2.len()); + for (s_1, s_2, d_1, d_2) in izip!(src_shorts_1, src_shorts_2, dst_shorts_1, dst_shorts_2) { + let (res_1, res_2) = self.apply(*s_1, *s_2); + d_1.write(res_1); + d_2.write(res_2); + } + for (s_1, s_2, d_1, d_2) in izip!(src_suffix_1, src_suffix_2, dst_suffix_1, dst_suffix_2) { + let (res_1, res_2) = self.apply(*s_1, *s_2); + d_1.write(res_1); + d_2.write(res_2); + } + } +} + +/// DIF (Decimation-In-Frequency) butterfly operation. +/// +/// Used in the *output-ordering* variant of NTT. +/// This butterfly computes: +/// ```text +/// output_1 = x1 + x2 +/// output_2 = (x1 - x2) * twiddle +/// ``` +/// The twiddle factor is applied after subtraction. +/// Suitable for DIF-style recursive transforms. +#[derive(Copy, Clone)] +pub struct DifButterfly(pub F); + +impl Butterfly for DifButterfly { + #[inline] + fn apply>(&self, x_1: PF, x_2: PF) -> (PF, PF) { + (x_1 + x_2, (x_1 - x_2) * self.0) + } +} + +/// DIF (Decimation-In-Frequency) butterfly operation where `x_2` is guaranteed to be zero. +/// +/// Useful in scenarios where the input has just been padded with zeros. +/// +/// Used in the *output-ordering* variant of NTT. +/// This butterfly computes: +/// ```text +/// output_1 = x1 +/// output_2 = x1 * twiddle +/// ``` +#[derive(Copy, Clone)] +pub struct DifButterflyZeros(pub F); + +impl Butterfly for DifButterflyZeros { + #[inline] + fn apply>(&self, x_1: PF, x_2: PF) -> (PF, PF) { + debug_assert!(x_2.as_slice().iter().all(|x| x.is_zero())); // Slightly convoluted but PF may not implement equality. + (x_1, x_1 * self.0) + } + + #[inline] + fn apply_to_rows(&self, row_1: &mut [F], row_2: &mut [F]) { + let (shorts_1, suffix_1) = F::Packing::pack_slice_with_suffix(row_1); + let (shorts_2, suffix_2) = F::Packing::pack_slice_with_suffix_mut(row_2); + debug_assert_eq!(shorts_1.len(), shorts_2.len()); + debug_assert_eq!(suffix_1.len(), suffix_2.len()); + for (x_1, x_2) in shorts_1.iter().zip(shorts_2) { + debug_assert!(x_2.as_slice().iter().all(|x| x.is_zero())); // Slightly convoluted but PF may not implement equality. + *x_2 = *x_1 * self.0; // x_2 is guaranteed to be zero, so we just set it to x_1 * twiddle. + } + for (x_1, x_2) in suffix_1.iter().zip(suffix_2) { + debug_assert!(x_2.is_zero()); + *x_2 = *x_1 * self.0; // x_2 is guaranteed to be zero, so we just set it to x_1 * twiddle. + } + } +} + +/// DIT (Decimation-In-Time) butterfly operation. +/// +/// Used in the *input-ordering* variant of NTT/FFT. +/// This butterfly computes: +/// ```text +/// output_1 = x1 + x2 * twiddle +/// output_2 = x1 - x2 * twiddle +/// ``` +/// The twiddle factor is applied to x2 before combining. +/// Suitable for DIT-style recursive transforms. +#[derive(Copy, Clone)] +pub struct DitButterfly(pub F); + +impl Butterfly for DitButterfly { + #[inline] + fn apply>(&self, x_1: PF, x_2: PF) -> (PF, PF) { + let x_2_twiddle = x_2 * self.0; + (x_1 + x_2_twiddle, x_1 - x_2_twiddle) + } +} + +/// Butterfly with no twiddle factor (`twiddle = 1`). +/// +/// This is used when no root-of-unity scaling is needed. +/// It works for either DIT or DIF, and is often used at +/// the final or base level of a transform tree. +/// +/// This butterfly computes: +/// ```text +/// - output_1 = x1 + x2 +/// - output_2 = x1 - x2 +/// ``` +#[derive(Copy, Clone)] +pub struct TwiddleFreeButterfly; + +impl Butterfly for TwiddleFreeButterfly { + #[inline] + fn apply>(&self, x_1: PF, x_2: PF) -> (PF, PF) { + (x_1 + x_2, x_1 - x_2) + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/lib.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/lib.rs new file mode 100644 index 00000000..6ef545c9 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/lib.rs @@ -0,0 +1,23 @@ +//! This crate contains some DFT implementations. + +#![no_std] + +extern crate alloc; + +mod butterflies; +mod naive; +mod radix_2_bowers; +mod radix_2_dit; +mod radix_2_dit_parallel; +mod radix_2_small_batch; +mod traits; +mod util; + +pub use butterflies::*; +pub use naive::*; +pub use radix_2_bowers::*; +pub use radix_2_dit::*; +pub use radix_2_dit_parallel::*; +pub use radix_2_small_batch::*; +pub use traits::*; +pub use util::*; diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/naive.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/naive.rs new file mode 100644 index 00000000..2822506a --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/naive.rs @@ -0,0 +1,108 @@ +use alloc::vec; + +use p3_field::TwoAdicField; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrix; +use p3_util::log2_strict_usize; + +use crate::TwoAdicSubgroupDft; + +#[derive(Default, Clone, Debug)] +pub struct NaiveDft; + +impl TwoAdicSubgroupDft for NaiveDft { + type Evaluations = RowMajorMatrix; + fn dft_batch(&self, mat: RowMajorMatrix) -> RowMajorMatrix { + let w = mat.width(); + let h = mat.height(); + let log_h = log2_strict_usize(h); + let g = F::two_adic_generator(log_h); + + let mut res = RowMajorMatrix::new(vec![F::ZERO; w * h], w); + for (res_r, point) in g.powers().take(h).enumerate() { + for (src_r, point_power) in point.powers().take(h).enumerate() { + for c in 0..w { + res.values[res_r * w + c] += point_power * mat.values[src_r * w + c] + } + } + } + + res + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use p3_baby_bear::BabyBear; + use p3_field::{Field, PrimeCharacteristicRing}; + use p3_goldilocks::Goldilocks; + use p3_matrix::dense::RowMajorMatrix; + use rand::SeedableRng; + use rand::rngs::SmallRng; + + use crate::{NaiveDft, TwoAdicSubgroupDft}; + + #[test] + fn basic() { + type F = BabyBear; + + // A few polynomials: + // 5 + 4x + // 2 + 3x + // 0 + let mat = RowMajorMatrix::new( + vec![ + F::from_u8(5), + F::from_u8(2), + F::ZERO, + F::from_u8(4), + F::from_u8(3), + F::ZERO, + ], + 3, + ); + + let dft = NaiveDft.dft_batch(mat); + // Expected evaluations on {1, -1}: + // 9, 1 + // 5, -1 + // 0, 0 + assert_eq!( + dft, + RowMajorMatrix::new( + vec![ + F::from_u8(9), + F::from_u8(5), + F::ZERO, + F::ONE, + F::NEG_ONE, + F::ZERO, + ], + 3, + ) + ) + } + + #[test] + fn dft_idft_consistency() { + type F = Goldilocks; + let mut rng = SmallRng::seed_from_u64(1); + let original = RowMajorMatrix::::rand(&mut rng, 8, 3); + let dft = NaiveDft.dft_batch(original.clone()); + let idft = NaiveDft.idft_batch(dft); + assert_eq!(original, idft); + } + + #[test] + fn coset_dft_idft_consistency() { + type F = Goldilocks; + let generator = F::GENERATOR; + let mut rng = SmallRng::seed_from_u64(1); + let original = RowMajorMatrix::::rand(&mut rng, 8, 3); + let dft = NaiveDft.coset_dft_batch(original.clone(), generator); + let idft = NaiveDft.coset_idft_batch(dft, generator); + assert_eq!(original, idft); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_bowers.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_bowers.rs new file mode 100644 index 00000000..029c7ebe --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_bowers.rs @@ -0,0 +1,129 @@ +use alloc::vec::Vec; + +use p3_field::{Field, Powers, PrimeCharacteristicRing, TwoAdicField}; +use p3_matrix::Matrix; +use p3_matrix::dense::{RowMajorMatrix, RowMajorMatrixViewMut}; +use p3_matrix::util::reverse_matrix_index_bits; +use p3_maybe_rayon::prelude::*; +use p3_util::{log2_strict_usize, reverse_bits, reverse_slice_index_bits}; + +use crate::TwoAdicSubgroupDft; +use crate::butterflies::{Butterfly, DifButterfly, DitButterfly, TwiddleFreeButterfly}; +use crate::util::divide_by_height; + +/// The Bowers G FFT algorithm. +/// See: "Improved Twiddle Access for Fast Fourier Transforms" +#[derive(Default, Clone)] +pub struct Radix2Bowers; + +impl TwoAdicSubgroupDft for Radix2Bowers { + type Evaluations = RowMajorMatrix; + + fn dft_batch(&self, mut mat: RowMajorMatrix) -> RowMajorMatrix { + reverse_matrix_index_bits(&mut mat); + bowers_g(&mut mat.as_view_mut()); + mat + } + + /// Compute the inverse DFT of each column in `mat`. + fn idft_batch(&self, mut mat: RowMajorMatrix) -> RowMajorMatrix { + bowers_g_t(&mut mat.as_view_mut()); + divide_by_height(&mut mat); + reverse_matrix_index_bits(&mut mat); + mat + } + + fn lde_batch(&self, mut mat: RowMajorMatrix, added_bits: usize) -> RowMajorMatrix { + bowers_g_t(&mut mat.as_view_mut()); + divide_by_height(&mut mat); + mat = mat.bit_reversed_zero_pad(added_bits); + bowers_g(&mut mat.as_view_mut()); + mat + } + fn coset_lde_batch( + &self, + mut mat: RowMajorMatrix, + added_bits: usize, + shift: F, + ) -> RowMajorMatrix { + let h = mat.height(); + let log_h = log2_strict_usize(h); + // It's cheaper to use div_2exp_u64 as this usually avoids an inversion. + // It's also cheaper to work in the PrimeSubfield whenever possible. + let h_inv_subfield = F::PrimeSubfield::ONE.div_2exp_u64(log_h as u64); + let h_inv = F::from_prime_subfield(h_inv_subfield); + + bowers_g_t(&mut mat.as_view_mut()); + + // Rescale coefficients in two ways: + // - divide by height (since we're doing an inverse DFT) + // - multiply by powers of the coset shift (see default coset LDE impl for an explanation) + let weights = Powers { + base: shift, + current: h_inv, + } + .take(h); + for (row, weight) in weights.enumerate() { + // reverse_bits because mat is encoded in bit-reversed order + mat.scale_row(reverse_bits(row, h), weight); + } + + mat = mat.bit_reversed_zero_pad(added_bits); + + bowers_g(&mut mat.as_view_mut()); + + mat + } +} + +/// Executes the Bowers G network. This is like a DFT, except it assumes the input is in +/// bit-reversed order. +fn bowers_g(mat: &mut RowMajorMatrixViewMut) { + let h = mat.height(); + let log_h = log2_strict_usize(h); + + let root = F::two_adic_generator(log_h); + let mut twiddles: Vec<_> = root.powers().take(h / 2).map(DifButterfly).collect(); + reverse_slice_index_bits(&mut twiddles); + + for log_half_block_size in 0..log_h { + butterfly_layer(mat, 1 << log_half_block_size, &twiddles) + } +} + +/// Executes the Bowers G^T network. This is like an inverse DFT, except we skip rescaling by +/// 1/height, and the output is bit-reversed. +fn bowers_g_t(mat: &mut RowMajorMatrixViewMut) { + let h = mat.height(); + let log_h = log2_strict_usize(h); + + let root_inv = F::two_adic_generator(log_h).inverse(); + let mut twiddles: Vec<_> = root_inv.powers().take(h / 2).map(DitButterfly).collect(); + reverse_slice_index_bits(&mut twiddles); + + for log_half_block_size in (0..log_h).rev() { + butterfly_layer(mat, 1 << log_half_block_size, &twiddles) + } +} + +fn butterfly_layer>( + mat: &mut RowMajorMatrixViewMut, + half_block_size: usize, + twiddles: &[B], +) { + mat.par_row_chunks_exact_mut(2 * half_block_size) + .enumerate() + .for_each(|(block, mut chunks)| { + let (mut hi_chunks, mut lo_chunks) = chunks.split_rows_mut(half_block_size); + hi_chunks + .par_rows_mut() + .zip(lo_chunks.par_rows_mut()) + .for_each(|(hi_chunk, lo_chunk)| { + if block == 0 { + TwiddleFreeButterfly.apply_to_rows(hi_chunk, lo_chunk) + } else { + twiddles[block].apply_to_rows(hi_chunk, lo_chunk); + } + }); + }); +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_dit.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_dit.rs new file mode 100644 index 00000000..9e6b368c --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_dit.rs @@ -0,0 +1,100 @@ +use alloc::collections::BTreeMap; +use alloc::vec::Vec; +use core::cell::RefCell; + +use p3_field::{Field, TwoAdicField}; +use p3_matrix::Matrix; +use p3_matrix::dense::{RowMajorMatrix, RowMajorMatrixViewMut}; +use p3_matrix::util::reverse_matrix_index_bits; +use p3_maybe_rayon::prelude::*; +use p3_util::log2_strict_usize; + +use crate::TwoAdicSubgroupDft; +use crate::butterflies::{Butterfly, DitButterfly, TwiddleFreeButterfly}; + +/// Radix-2 Decimation-in-Time FFT over a two-adic subgroup. +/// +/// This struct implements a fast Fourier transform (FFT) using the Radix-2 +/// Decimation-in-Time (DIT) algorithm over a two-adic multiplicative subgroup of a finite field. +/// It is optimized for a batch setting where multiple FFT's are being computed simultaneously. +/// +/// Internally, the implementation memoizes twiddle factors (powers of the root of unity) +/// for reuse across multiple transforms. This avoids redundant computation +/// when performing FFTs of the same size. +#[derive(Default, Clone, Debug)] +pub struct Radix2Dit { + /// Memoized twiddle factors indexed by `log2(n)`, where `n` is the DFT length. + /// + /// This allows fast lookup and reuse of previously computed twiddle values + /// (powers of a two-adic generator), which are expensive to recompute. + /// + /// `RefCell` is used to enable interior mutability for caching purposes. + twiddles: RefCell>>, +} + +impl TwoAdicSubgroupDft for Radix2Dit { + type Evaluations = RowMajorMatrix; + + fn dft_batch(&self, mut mat: RowMajorMatrix) -> RowMajorMatrix { + let h = mat.height(); + let log_h = log2_strict_usize(h); + + // Compute twiddle factors, or take memoized ones if already available. + let mut twiddles_ref_mut = self.twiddles.borrow_mut(); + let twiddles = twiddles_ref_mut.entry(log_h).or_insert_with(|| { + let root = F::two_adic_generator(log_h); + root.powers().take(1 << log_h).collect() + }); + + // DIT butterfly + reverse_matrix_index_bits(&mut mat); + for layer in 0..log_h { + dit_layer(&mut mat.as_view_mut(), layer, twiddles); + } + mat + } +} + +/// Applies one layer of the Radix-2 DIT FFT butterfly network. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. Uses a `TwiddleFreeButterfly` for the first pair and `DitButterfly` +/// with precomputed twiddles for the rest. +/// +/// # Arguments +/// - `mat`: Mutable matrix view with height as a power of two. +/// - `layer`: Index of the current FFT layer (starting at 0). +/// - `twiddles`: Precomputed twiddle factors for this layer. +fn dit_layer(mat: &mut RowMajorMatrixViewMut<'_, F>, layer: usize, twiddles: &[F]) { + // Get the number of rows in the matrix (must be a power of two) + let h = mat.height(); + // Compute reversed layer index to access twiddle indices correctly + let log_h = log2_strict_usize(h); + let layer_rev = log_h - 1 - layer; + + // Each butterfly operates on 2 rows; this is the number of rows in half a block + let half_block_size = 1 << layer; + // Each block contains 2^layer * 2 rows; full size of the butterfly block + let block_size = half_block_size * 2; + + // Process the matrix in blocks of rows of size `block_size` + mat.par_row_chunks_exact_mut(block_size) + .for_each(|mut block_chunks| { + // Split each block vertically into top (hi) and bottom (lo) halves + let (mut hi_chunks, mut lo_chunks) = block_chunks.split_rows_mut(half_block_size); + // For each pair of rows (hi, lo), apply a butterfly + hi_chunks + .par_rows_mut() + .zip(lo_chunks.par_rows_mut()) + .enumerate() + .for_each(|(ind, (hi_chunk, lo_chunk))| { + if ind == 0 { + // The first pair doesn't require a twiddle factor + TwiddleFreeButterfly.apply_to_rows(hi_chunk, lo_chunk) + } else { + // Apply DIT butterfly using the twiddle factor at index `ind << layer_rev` + DitButterfly(twiddles[ind << layer_rev]).apply_to_rows(hi_chunk, lo_chunk) + } + }); + }); +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_dit_parallel.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_dit_parallel.rs new file mode 100644 index 00000000..a2d85251 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_dit_parallel.rs @@ -0,0 +1,506 @@ +use alloc::collections::BTreeMap; +use alloc::slice; +use alloc::vec::Vec; +use core::cell::RefCell; +use core::mem::{MaybeUninit, transmute}; + +use itertools::{Itertools, izip}; +use p3_field::integers::QuotientMap; +use p3_field::{Field, Powers, TwoAdicField}; +use p3_matrix::Matrix; +use p3_matrix::bitrev::{BitReversalPerm, BitReversedMatrixView, BitReversibleMatrix}; +use p3_matrix::dense::{RowMajorMatrix, RowMajorMatrixView, RowMajorMatrixViewMut}; +use p3_matrix::util::reverse_matrix_index_bits; +use p3_maybe_rayon::prelude::*; +use p3_util::{log2_strict_usize, reverse_bits_len, reverse_slice_index_bits}; + +use crate::TwoAdicSubgroupDft; +use crate::butterflies::{Butterfly, DitButterfly}; + +/// A parallel FFT algorithm which divides a butterfly network's layers into two halves. +/// +/// For the first half, we apply a butterfly network with smaller blocks in earlier layers, +/// i.e. either DIT or Bowers G. Then we bit-reverse, and for the second half, we continue executing +/// the same network but in bit-reversed order. This way we're always working with small blocks, +/// so within each half, we can have a certain amount of parallelism with no cross-thread +/// communication. +#[derive(Default, Clone, Debug)] +pub struct Radix2DitParallel { + /// Twiddles based on roots of unity, used in the forward DFT. + twiddles: RefCell>>, + + /// A map from `(log_h, shift)` to forward DFT twiddles with that coset shift baked in. + #[allow(clippy::type_complexity)] + coset_twiddles: RefCell>>>, + + /// Twiddles based on inverse roots of unity, used in the inverse DFT. + inverse_twiddles: RefCell>>, +} + +/// A pair of vectors, one with twiddle factors in their natural order, the other bit-reversed. +#[derive(Default, Clone, Debug)] +struct VectorPair { + twiddles: Vec, + bitrev_twiddles: Vec, +} +fn compute_twiddles(log_h: usize) -> VectorPair { + let half_h = (1 << log_h) >> 1; + let root = F::two_adic_generator(log_h); + let twiddles: Vec = root.powers().take(half_h).collect(); + let mut bit_reversed_twiddles = twiddles.clone(); + reverse_slice_index_bits(&mut bit_reversed_twiddles); + VectorPair { + twiddles, + bitrev_twiddles: bit_reversed_twiddles, + } +} +fn compute_coset_twiddles(log_h: usize, shift: F) -> Vec> { + // In general either div_floor or div_ceil would work, but here we prefer div_ceil because it + // lets us assume below that the "first half" of the network has at least one layer of + // butterflies, even in the case of log_h = 1. + let mid = log_h.div_ceil(2); + let h = 1 << log_h; + let root = F::two_adic_generator(log_h); + + (0..log_h) + .map(|layer| { + let shift_power = shift.exp_power_of_2(layer); + let powers = Powers { + base: root.exp_power_of_2(layer), + current: shift_power, + }; + let mut twiddles: Vec<_> = powers.take(h >> (layer + 1)).collect(); + let layer_rev = log_h - 1 - layer; + if layer_rev >= mid { + reverse_slice_index_bits(&mut twiddles); + } + twiddles + }) + .collect() +} +fn compute_inverse_twiddles(log_h: usize) -> VectorPair { + let half_h = (1 << log_h) >> 1; + let root_inv = F::two_adic_generator(log_h).inverse(); + let twiddles: Vec = root_inv.powers().take(half_h).collect(); + let mut bit_reversed_twiddles = twiddles.clone(); + + // In the middle of the coset LDE, we're in bit-reversed order. + reverse_slice_index_bits(&mut bit_reversed_twiddles); + + VectorPair { + twiddles, + bitrev_twiddles: bit_reversed_twiddles, + } +} + +impl TwoAdicSubgroupDft for Radix2DitParallel { + type Evaluations = BitReversedMatrixView>; + + fn dft_batch(&self, mut mat: RowMajorMatrix) -> Self::Evaluations { + let h = mat.height(); + let log_h = log2_strict_usize(h); + + // Compute twiddle factors, or take memoized ones if already available. + let mut twiddles_ref_mut = self.twiddles.borrow_mut(); + let twiddles = twiddles_ref_mut + .entry(log_h) + .or_insert_with(|| compute_twiddles(log_h)); + + let mid = log_h.div_ceil(2); + + // The first half looks like a normal DIT. + reverse_matrix_index_bits(&mut mat); + first_half(&mut mat, mid, &twiddles.twiddles); + + // For the second half, we flip the DIT, working in bit-reversed order. + reverse_matrix_index_bits(&mut mat); + second_half(&mut mat, mid, &twiddles.bitrev_twiddles, None); + + mat.bit_reverse_rows() + } + fn coset_lde_batch( + &self, + mut mat: RowMajorMatrix, + added_bits: usize, + shift: F, + ) -> Self::Evaluations { + let w = mat.width; + let h = mat.height(); + let log_h = log2_strict_usize(h); + let mid = log_h.div_ceil(2); + + let mut inverse_twiddles_ref_mut = self.inverse_twiddles.borrow_mut(); + let inverse_twiddles = inverse_twiddles_ref_mut + .entry(log_h) + .or_insert_with(|| compute_inverse_twiddles(log_h)); + + // The first half looks like a normal DIT. + reverse_matrix_index_bits(&mut mat); + first_half(&mut mat, mid, &inverse_twiddles.twiddles); + + // For the second half, we flip the DIT, working in bit-reversed order. + reverse_matrix_index_bits(&mut mat); + // We'll also scale by 1/h, as per the usual inverse DFT algorithm. + // If F isn't a PrimeField, (and is thus an extension field) it's much cheaper to + // invert in F::PrimeSubfield. + let h_inv_subfield = F::PrimeSubfield::from_int(h).try_inverse(); + let scale = h_inv_subfield.map(F::from_prime_subfield); + second_half(&mut mat, mid, &inverse_twiddles.bitrev_twiddles, scale); + // We skip the final bit-reversal, since the next FFT expects bit-reversed input. + + let lde_elems = w * (h << added_bits); + let elems_to_add = lde_elems - w * h; + mat.values.reserve_exact(elems_to_add); + + let g_big = F::two_adic_generator(log_h + added_bits); + + let mat_ptr = mat.values.as_mut_ptr(); + let rest_ptr = unsafe { (mat_ptr as *mut MaybeUninit).add(w * h) }; + let first_slice: &mut [F] = unsafe { slice::from_raw_parts_mut(mat_ptr, w * h) }; + let rest_slice: &mut [MaybeUninit] = + unsafe { slice::from_raw_parts_mut(rest_ptr, lde_elems - w * h) }; + let mut first_coset_mat = RowMajorMatrixViewMut::new(first_slice, w); + let mut rest_cosets_mat = rest_slice + .chunks_exact_mut(w * h) + .map(|slice| RowMajorMatrixViewMut::new(slice, w)) + .collect_vec(); + + for coset_idx in 1..(1 << added_bits) { + let total_shift = g_big.exp_u64(coset_idx as u64) * shift; + let coset_idx = reverse_bits_len(coset_idx, added_bits); + let dest = &mut rest_cosets_mat[coset_idx - 1]; // - 1 because we removed the first matrix. + coset_dft_oop(self, &first_coset_mat.as_view(), dest, total_shift); + } + + // Now run a forward DFT on the very first coset, this time in-place. + coset_dft(self, &mut first_coset_mat.as_view_mut(), shift); + + // SAFETY: We wrote all values above. + unsafe { + mat.values.set_len(lde_elems); + } + BitReversalPerm::new_view(mat) + } +} +fn coset_dft( + dft: &Radix2DitParallel, + mat: &mut RowMajorMatrixViewMut, + shift: F, +) { + let log_h = log2_strict_usize(mat.height()); + let mid = log_h.div_ceil(2); + + let mut twiddles_ref_mut = dft.coset_twiddles.borrow_mut(); + let twiddles = twiddles_ref_mut + .entry((log_h, shift)) + .or_insert_with(|| compute_coset_twiddles(log_h, shift)); + + // The first half looks like a normal DIT. + first_half_general(mat, mid, twiddles); + + // For the second half, we flip the DIT, working in bit-reversed order. + reverse_matrix_index_bits(mat); + + second_half_general(mat, mid, twiddles); +} + +/// Like `coset_dft`, except out-of-place. +fn coset_dft_oop( + dft: &Radix2DitParallel, + src: &RowMajorMatrixView, + dst_maybe: &mut RowMajorMatrixViewMut>, + shift: F, +) { + assert_eq!(src.dimensions(), dst_maybe.dimensions()); + + let log_h = log2_strict_usize(dst_maybe.height()); + + if log_h == 0 { + // This is an edge case where first_half_general_oop doesn't work, as it expects there to be + // at least one layer in the network, so we just copy instead. + let src_maybe = unsafe { + transmute::<&RowMajorMatrixView, &RowMajorMatrixView>>(src) + }; + dst_maybe.copy_from(src_maybe); + return; + } + + let mid = log_h.div_ceil(2); + + let mut twiddles_ref_mut = dft.coset_twiddles.borrow_mut(); + let twiddles = twiddles_ref_mut + .entry((log_h, shift)) + .or_insert_with(|| compute_coset_twiddles(log_h, shift)); + + // The first half looks like a normal DIT. + first_half_general_oop(src, dst_maybe, mid, twiddles); + + // dst is now initialized. + let dst = unsafe { + transmute::<&mut RowMajorMatrixViewMut>, &mut RowMajorMatrixViewMut>( + dst_maybe, + ) + }; + + // For the second half, we flip the DIT, working in bit-reversed order. + reverse_matrix_index_bits(dst); + + second_half_general(dst, mid, twiddles); +} + +/// This can be used as the first half of a DIT butterfly network. +fn first_half(mat: &mut RowMajorMatrix, mid: usize, twiddles: &[F]) { + let log_h = log2_strict_usize(mat.height()); + + // max block size: 2^mid + mat.par_row_chunks_exact_mut(1 << mid) + .for_each(|mut submat| { + let mut backwards = false; + for layer in 0..mid { + let layer_rev = log_h - 1 - layer; + let layer_pow = 1 << layer_rev; + dit_layer( + &mut submat, + layer, + twiddles.iter().copied().step_by(layer_pow), + backwards, + ); + backwards = !backwards; + } + }); +} + +/// Like `first_half`, except supporting different twiddle factors per layer, enabling coset shifts +/// to be baked into them. +fn first_half_general( + mat: &mut RowMajorMatrixViewMut, + mid: usize, + twiddles: &[Vec], +) { + let log_h = log2_strict_usize(mat.height()); + mat.par_row_chunks_exact_mut(1 << mid) + .for_each(|mut submat| { + let mut backwards = false; + for layer in 0..mid { + let layer_rev = log_h - 1 - layer; + dit_layer( + &mut submat, + layer, + twiddles[layer_rev].iter().copied(), + backwards, + ); + backwards = !backwards; + } + }); +} + +/// Like `first_half_general`, except out-of-place. +/// +/// Assumes there's at least one layer in the network, i.e. `src.height() > 1`. +/// Undefined behavior otherwise. +fn first_half_general_oop( + src: &RowMajorMatrixView, + dst_maybe: &mut RowMajorMatrixViewMut>, + mid: usize, + twiddles: &[Vec], +) { + let log_h = log2_strict_usize(src.height()); + src.par_row_chunks_exact(1 << mid) + .zip(dst_maybe.par_row_chunks_exact_mut(1 << mid)) + .for_each(|(src_submat, mut dst_submat_maybe)| { + debug_assert_eq!(src_submat.dimensions(), dst_submat_maybe.dimensions()); + + // The first layer is special, done out-of-place. + // (Recall from the mid definition that there must be at least one layer here.) + let layer_rev = log_h - 1; + dit_layer_oop( + &src_submat, + &mut dst_submat_maybe, + 0, + twiddles[layer_rev].iter().copied(), + ); + + // submat is now initialized. + let mut dst_submat = unsafe { + transmute::>, RowMajorMatrixViewMut>( + dst_submat_maybe, + ) + }; + + // Subsequent layers. + let mut backwards = true; + for layer in 1..mid { + let layer_rev = log_h - 1 - layer; + dit_layer( + &mut dst_submat, + layer, + twiddles[layer_rev].iter().copied(), + backwards, + ); + backwards = !backwards; + } + }); +} + +/// This can be used as the second half of a DIT butterfly network. It works in bit-reversed order. +/// +/// The optional `scale` parameter is used to scale the matrix by a constant factor. Normally that +/// would be a separate step, but it's best to merge it into a butterfly network to avoid a +/// separate pass through main memory. +#[inline(always)] // To avoid branch on scale +fn second_half( + mat: &mut RowMajorMatrix, + mid: usize, + twiddles_rev: &[F], + scale: Option, +) { + let log_h = log2_strict_usize(mat.height()); + + // max block size: 2^(log_h - mid) + mat.par_row_chunks_exact_mut(1 << (log_h - mid)) + .enumerate() + .for_each(|(thread, mut submat)| { + let mut backwards = false; + if let Some(scale) = scale { + submat.scale(scale); + } + for layer in mid..log_h { + let first_block = thread << (layer - mid); + dit_layer_rev( + &mut submat, + log_h, + layer, + twiddles_rev[first_block..].iter().copied(), + backwards, + ); + backwards = !backwards; + } + }); +} + +/// Like `second_half`, except supporting different twiddle factors per layer, enabling coset shifts +/// to be baked into them. +fn second_half_general( + mat: &mut RowMajorMatrixViewMut, + mid: usize, + twiddles_rev: &[Vec], +) { + let log_h = log2_strict_usize(mat.height()); + mat.par_row_chunks_exact_mut(1 << (log_h - mid)) + .enumerate() + .for_each(|(thread, mut submat)| { + let mut backwards = false; + for layer in mid..log_h { + let layer_rev = log_h - 1 - layer; + let first_block = thread << (layer - mid); + dit_layer_rev( + &mut submat, + log_h, + layer, + twiddles_rev[layer_rev][first_block..].iter().copied(), + backwards, + ); + backwards = !backwards; + } + }); +} + +/// One layer of a DIT butterfly network. +fn dit_layer( + submat: &mut RowMajorMatrixViewMut<'_, F>, + layer: usize, + twiddles: impl Iterator + Clone, + backwards: bool, +) { + let half_block_size = 1 << layer; + let block_size = half_block_size * 2; + let width = submat.width(); + debug_assert!(submat.height() >= block_size); + + let process_block = |block: &mut [F]| { + let (lows, highs) = block.split_at_mut(half_block_size * width); + + for (lo, hi, twiddle) in izip!( + lows.chunks_mut(width), + highs.chunks_mut(width), + twiddles.clone() + ) { + DitButterfly(twiddle).apply_to_rows(lo, hi); + } + }; + + let blocks = submat.values.chunks_mut(block_size * width); + if backwards { + for block in blocks.rev() { + process_block(block); + } + } else { + for block in blocks { + process_block(block); + } + } +} + +/// One layer of a DIT butterfly network. +fn dit_layer_oop( + src: &RowMajorMatrixView, + dst: &mut RowMajorMatrixViewMut<'_, MaybeUninit>, + layer: usize, + twiddles: impl Iterator + Clone, +) { + debug_assert_eq!(src.dimensions(), dst.dimensions()); + let half_block_size = 1 << layer; + let block_size = half_block_size * 2; + let width = dst.width(); + debug_assert!(dst.height() >= block_size); + + let src_chunks = src.values.chunks(block_size * width); + let dst_chunks = dst.values.chunks_mut(block_size * width); + for (src_block, dst_block) in src_chunks.zip(dst_chunks) { + let (src_lows, src_highs) = src_block.split_at(half_block_size * width); + let (dst_lows, dst_highs) = dst_block.split_at_mut(half_block_size * width); + + for (src_lo, dst_lo, src_hi, dst_hi, twiddle) in izip!( + src_lows.chunks(width), + dst_lows.chunks_mut(width), + src_highs.chunks(width), + dst_highs.chunks_mut(width), + twiddles.clone() + ) { + DitButterfly(twiddle).apply_to_rows_oop(src_lo, dst_lo, src_hi, dst_hi); + } + } +} + +/// Like `dit_layer`, except the matrix and twiddles are encoded in bit-reversed order. +/// This can also be viewed as a layer of the Bowers G^T network. +fn dit_layer_rev( + submat: &mut RowMajorMatrixViewMut<'_, F>, + log_h: usize, + layer: usize, + twiddles_rev: impl DoubleEndedIterator + ExactSizeIterator, + backwards: bool, +) { + let layer_rev = log_h - 1 - layer; + + let half_block_size = 1 << layer_rev; + let block_size = half_block_size * 2; + let width = submat.width(); + debug_assert!(submat.height() >= block_size); + + let blocks_and_twiddles = submat + .values + .chunks_mut(block_size * width) + .zip(twiddles_rev); + if backwards { + for (block, twiddle) in blocks_and_twiddles.rev() { + let (lo, hi) = block.split_at_mut(half_block_size * width); + DitButterfly(twiddle).apply_to_rows(lo, hi) + } + } else { + for (block, twiddle) in blocks_and_twiddles { + let (lo, hi) = block.split_at_mut(half_block_size * width); + DitButterfly(twiddle).apply_to_rows(lo, hi) + } + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_small_batch.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_small_batch.rs new file mode 100644 index 00000000..3a94113d --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/radix_2_small_batch.rs @@ -0,0 +1,1088 @@ +//! An FFT implementation optimized for small batch sizes. + +use alloc::vec::Vec; +use core::cell::RefCell; +use core::iter; + +use itertools::Itertools; +use p3_field::{Field, PackedField, PackedValue, TwoAdicField, scale_slice_in_place_single_core}; +use p3_matrix::Matrix; +use p3_matrix::dense::{RowMajorMatrix, RowMajorMatrixViewMut}; +use p3_matrix::util::reverse_matrix_index_bits; +use p3_maybe_rayon::prelude::*; +use p3_util::{flatten_to_base, log2_strict_usize, reverse_slice_index_bits}; + +use crate::{ + Butterfly, DifButterfly, DifButterflyZeros, DitButterfly, TwiddleFreeButterfly, + TwoAdicSubgroupDft, +}; + +/// The number of layers to compute in each parallelization. +const LAYERS_PER_GROUP: usize = 3; + +/// An FFT algorithm which divides a butterfly network's layers into two halves. +/// +/// Unlike other FFT algorithms, this algorithm is optimized for small batch sizes. +/// It also stores its twiddle factors and only re-computes if it is asked to do a +/// larger FFT. +/// +/// Instead of parallelizing across rows, this algorithm parallelizes across groups of rows +/// with the same twiddle factors. This allows it to make use of field packings far more than +/// the standard methods even for low width matrices. Once the chunk size is small enough, it +/// computes a large set of layers fully on a single thread, which avoids the overhead of +/// passing data between threads. +#[derive(Default, Clone, Debug)] +pub struct Radix2DFTSmallBatch { + /// Memoized twiddle factors for each length log_n. + /// + /// For each `i`, `twiddles[i]` contains a list of twiddles stored in + /// bit reversed order. The final set of twiddles `twiddles[-1]` is the + /// one element vectors `[1]` and more general `twiddles[-i]` has length `2^i`. + /// + /// TODO: The use of RefCell means this can't be shared across + /// threads; consider using RwLock or finding a better design + /// instead. + twiddles: RefCell>>, + + /// Similar to `twiddles`, but stored the inverses used for the inverse fft. + inv_twiddles: RefCell>>, +} + +impl Radix2DFTSmallBatch { + /// Create a new `Radix2DFTSmallBatch` instance with precomputed twiddles for the given size. + /// + /// The input `n` should be a power of two, representing the maximal FFT size you expect to handle. + pub fn new(n: usize) -> Self { + let res = Self { + twiddles: RefCell::default(), + inv_twiddles: RefCell::default(), + }; + res.update_twiddles(n); + res + } + + /// Given a field element `gen` of order n where `n = 2^lg_n`, + /// return a vector of vectors `table` where table[i] is the + /// vector of twiddle factors for an fft of length n/2^i. The + /// values g_i^k for k >= i/2 are skipped as these are just the + /// negatives of the other roots (using g_i^{i/2} = -1). The + /// value gen^0 = 1 is included to aid consistency between the + /// packed and non-packed variants. + fn roots_of_unity_table(&self, n: usize) -> Vec> { + let lg_n = log2_strict_usize(n); + let generator = F::two_adic_generator(lg_n); + let half_n = 1 << (lg_n - 1); + // nth_roots = [1, g, g^2, g^3, ..., g^{n/2 - 1}] + let nth_roots: Vec<_> = generator.powers().take(half_n).collect(); + + (0..lg_n) + .map(|i| nth_roots.iter().step_by(1 << i).copied().collect()) + .collect() + } + + /// Compute twiddle and inv_twiddle factors, or take memoized ones if already available. + fn update_twiddles(&self, fft_len: usize) { + // TODO: This recomputes the entire table from scratch if we + // need it to be larger, which is wasteful. + + // roots_of_unity_table(fft_len) returns a vector of twiddles of length log_2(fft_len). + let curr_max_fft_len = 1 << self.twiddles.borrow().len(); + if fft_len > curr_max_fft_len { + let mut new_twiddles = self.roots_of_unity_table(fft_len); + let mut new_inv_twiddles: Vec> = new_twiddles + .iter() + .map(|ts| { + // The first twiddle is still one, instead of inverting, we can + // just reverse and negate. + iter::once(F::ONE) + .chain(ts[1..].iter().rev().map(|&f| -f)) + .collect() + }) + .collect(); + + new_twiddles.iter_mut().for_each(|ts| { + reverse_slice_index_bits(ts); + }); + new_inv_twiddles.iter_mut().for_each(|ts| { + reverse_slice_index_bits(ts); + }); + + self.twiddles.replace(new_twiddles); + self.inv_twiddles.replace(new_inv_twiddles); + } + } +} + +impl TwoAdicSubgroupDft for Radix2DFTSmallBatch +where + F: TwoAdicField, +{ + type Evaluations = RowMajorMatrix; + + fn dft_batch(&self, mut mat: RowMajorMatrix) -> Self::Evaluations { + let h = mat.height(); + let w = mat.width(); + let log_h = log2_strict_usize(h); + + self.update_twiddles(h); + let root_table = self.twiddles.borrow(); + let len = root_table.len(); + let root_table = &root_table[len - log_h..]; + + // The strategy will be to do a standard round-by-round parallelization + // until the chunk size is smaller than `num_par_rows * mat.width()` after which we + // send `num_par_rows` chunks to each thread and do the remainder of the + // fft without transferring any more data between threads. + let num_par_rows = estimate_num_rows_in_l1::(h, w); + let log_num_par_rows = log2_strict_usize(num_par_rows); + let chunk_size = num_par_rows * w; + + // For the layers involving blocks larger than `num_par_rows`, we will + // parallelize across the blocks. + + // We do `LAYERS_PER_GROUP` layers of the DFT at once, to minimize how much data we need to transfer + // between threads. + for (twiddles_0, twiddles_1, twiddles_2) in + root_table[log_num_par_rows..].iter().rev().tuples() + { + dit_layer_par_triple(&mut mat.as_view_mut(), twiddles_0, twiddles_1, twiddles_2); + } + + // If the total number of layers is not a multiple of `LAYERS_PER_GROUP`, + // we need to handle the remaining layers separately. + if (log_h - log_num_par_rows) % LAYERS_PER_GROUP == 1 { + dit_layer_par(&mut mat.as_view_mut(), &root_table[log_num_par_rows]) + } else if (log_h - log_num_par_rows) % LAYERS_PER_GROUP == 2 { + dit_layer_par_double( + &mut mat.as_view_mut(), + &root_table[log_num_par_rows + 1], + &root_table[log_num_par_rows], + ) + } + + // Once the blocks are small enough, we can split the matrix + // into chunks of size `chunk_size` and process them in parallel. + // This avoids passing data between threads, which can be expensive. + par_remaining_layers(&mut mat.values, chunk_size, &root_table[..log_num_par_rows]); + + // Finally we bit-reverse the matrix to ensure the output is in the correct order. + reverse_matrix_index_bits(&mut mat); + mat + } + + fn idft_batch(&self, mut mat: RowMajorMatrix) -> RowMajorMatrix { + let h = mat.height(); + let w = mat.width(); + let log_h = log2_strict_usize(h); + + self.update_twiddles(h); + let root_table = self.inv_twiddles.borrow(); + let len = root_table.len(); + let root_table = &root_table[len - log_h..]; + + // Find the number of rows which can roughly fit in L1 cache. + // The strategy is literally the same as `dft_batch` but in reverse. + // We start by moving `num_par_rows` rows onto each thread and doing + // `num_par_rows` layers of the DFT. After this we recombine and do + // a standard round-by-round parallelization for the remaining layers. + let num_par_rows = estimate_num_rows_in_l1::(h, w); + let log_num_par_rows = log2_strict_usize(num_par_rows); + let chunk_size = num_par_rows * w; + + // Need to start by bit-reversing the matrix. + reverse_matrix_index_bits(&mut mat); + + // For the initial blocks, they are small enough that we can split the matrix + // into chunks of size `chunk_size` and process them in parallel. + // This avoids passing data between threads, which can be expensive. + // We also divide by the height of the matrix while the data is nicely partitioned + // on each core. + par_initial_layers( + &mut mat.values, + chunk_size, + &root_table[..log_num_par_rows], + log_h, + ); + + // For the layers involving blocks larger than `num_par_rows`, we will + // parallelize across the blocks. + + // If the total number of layers is not a multiple of `LAYERS_PER_GROUP`, + // we need to handle the remaining layers separately. + let corr = (log_h - log_num_par_rows) % LAYERS_PER_GROUP; + match corr { + 1 => { + dif_layer_par(&mut mat.as_view_mut(), &root_table[log_num_par_rows]); + } + 2 => { + dif_layer_par_double( + &mut mat.as_view_mut(), + &root_table[log_num_par_rows], + &root_table[log_num_par_rows + 1], + ); + } + _ => {} + } + + // We do `LAYERS_PER_GROUP` layers of the DFT at once, to minimize how much data we need to transfer + // between threads. + for (twiddles_0, twiddles_1, twiddles_2) in + root_table[(log_num_par_rows + corr)..].iter().tuples() + { + dif_layer_par_triple(&mut mat.as_view_mut(), twiddles_0, twiddles_1, twiddles_2); + } + + mat + } + + fn coset_lde_batch( + &self, + mut mat: RowMajorMatrix, + added_bits: usize, + shift: F, + ) -> Self::Evaluations { + let h = mat.height(); + let w = mat.width(); + let log_h = log2_strict_usize(h); + + self.update_twiddles(h << added_bits); + let root_table = self.twiddles.borrow(); + let inv_root_table = self.inv_twiddles.borrow(); + let len = root_table.len(); + + let root_table = &root_table[len - (log_h + added_bits)..]; + let inv_root_table = &inv_root_table[len - log_h..]; + let output_height = h << added_bits; + + // The matrix which we will use to store the output. + let output_values = F::zero_vec(output_height * w); + let mut output = RowMajorMatrix::new(output_values, w); + + // The strategy is reasonably straightforward. + // The rough idea is we want to squash together the dft and idft code which will + // cancel out the `reverse_matrix_index_bits`. + + // This lets us do all of the inner layers on a single thread reducing the amount + // of data we need to transfer. + + // For technical reasons, we need to swap the twiddle factors, using the inverse + // twiddles for the initial layers and the normal twiddles for the final layers. + // This lets us interpret the initial transformation as the idft giving us coefficients + // and the final transformation as the dft giving us evaluations. + + // Find the number of rows which can roughly fit in L1 cache. + // The strategy will be to do a standard round-by-round parallelization + // until the chunk size is smaller than `num_par_rows * mat.width()` after which we + // send `num_par_rows` chunks to each thread and do the remainder of the + // fft without transferring any more data between threads. + let num_par_rows = estimate_num_rows_in_l1::(h, w); + let num_inner_dit_layers = log2_strict_usize(num_par_rows); + let num_inner_dif_layers = num_inner_dit_layers + added_bits; + + // We will do large DFT/iDFT layers in batches of `LAYERS_PER_GROUP`. If the number of large layers + // is not a multiple of `LAYERS_PER_GROUP`, we will need to handle the remaining layers separately. + let corr = (log_h - num_inner_dit_layers) % LAYERS_PER_GROUP; + + // We do `LAYERS_PER_GROUP` layers of the DFT at once, to minimize how much data we need to transfer + // between threads. + for (twiddles_0, twiddles_1, twiddles_2) in + inv_root_table[num_inner_dit_layers..].iter().rev().tuples() + { + dit_layer_par_triple(&mut mat.as_view_mut(), twiddles_0, twiddles_1, twiddles_2); + } + + // If the total number of layers is not a multiple of `LAYERS_PER_GROUP`, + // we need to handle the remaining layers separately. + match corr { + 1 => { + dit_layer_par( + &mut mat.as_view_mut(), + &inv_root_table[num_inner_dit_layers], + ); + } + 2 => { + dit_layer_par_double( + &mut mat.as_view_mut(), + &inv_root_table[num_inner_dit_layers + 1], + &inv_root_table[num_inner_dit_layers], + ); + } + _ => {} + } + + // Now do all the inner layers at once. This does the final `log_num_par_rows` of + // the initial transformation, then copies the values of mat to output, scales then + // and does the first `log_num_par_rows + added_bits` layers of the final transformation. + par_middle_layers( + &mut mat.as_view_mut(), + &mut output.as_view_mut(), + num_par_rows, + &root_table[..(num_inner_dif_layers)], + &inv_root_table[..num_inner_dit_layers], + added_bits, + shift, + ); + + // If the total number of layers is not a multiple of `LAYERS_PER_GROUP`, + // we need to handle the remaining layers separately. + match corr { + 1 => { + dif_layer_par(&mut output.as_view_mut(), &root_table[num_inner_dif_layers]); + } + 2 => { + dif_layer_par_double( + &mut output.as_view_mut(), + &root_table[num_inner_dif_layers], + &root_table[num_inner_dif_layers + 1], + ); + } + _ => {} + } + + // We do `LAYERS_PER_GROUP` layers of the DFT at once, to minimize how much data we need to transfer + // between threads. + for (twiddles_0, twiddles_1, twiddles_2) in + root_table[(num_inner_dif_layers + corr)..].iter().tuples() + { + dif_layer_par_triple( + &mut output.as_view_mut(), + twiddles_0, + twiddles_1, + twiddles_2, + ); + } + + output + } +} + +/// Applies one layer of the Radix-2 DIT FFT butterfly network making use of parallelization. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. Uses a `TwiddleFreeButterfly` for the first pair and `DitButterfly` +/// with precomputed twiddles for the rest. +/// +/// Each block is processed in parallel, if the blocks are large enough they themselves +/// are split into parallel sub-blocks. +/// +/// # Arguments +/// - `mat`: Mutable matrix whose height is a power of two. +/// - `twiddles`: Precomputed twiddle factors for this layer. +#[inline] +fn dit_layer_par(mat: &mut RowMajorMatrixViewMut, twiddles: &[F]) { + debug_assert!( + mat.height() % twiddles.len() == 0, + "Matrix height must be divisible by the number of twiddles" + ); + let size = mat.values.len(); + let num_blocks = twiddles.len(); + + let outer_block_size = size / num_blocks; + let half_outer_block_size = outer_block_size / 2; + + mat.values + .par_chunks_exact_mut(outer_block_size) + .enumerate() + .for_each(|(ind, block)| { + // Split each block vertically into top (hi) and bottom (lo) halves + let (hi_chunk, lo_chunk) = block.split_at_mut(half_outer_block_size); + + // If num_blocks is small, we probably are not using all available threads. + let num_threads = current_num_threads(); + let inner_block_size = size / (2 * num_blocks).max(num_threads); + + hi_chunk + .par_chunks_mut(inner_block_size) + .zip(lo_chunk.par_chunks_mut(inner_block_size)) + .for_each(|(hi_chunk, lo_chunk)| { + if ind == 0 { + // The first pair doesn't require a twiddle factor + TwiddleFreeButterfly.apply_to_rows(hi_chunk, lo_chunk); + } else { + // Apply DIT butterfly using the twiddle factor at index `ind - 1` + DitButterfly(twiddles[ind]).apply_to_rows(hi_chunk, lo_chunk); + } + }); + }); +} + +/// Applies one layer of the Radix-2 DIF FFT butterfly network making use of parallelization. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. Uses a `TwiddleFreeButterfly` for the first pair and `DifButterfly` +/// with precomputed twiddles for the rest. +/// +/// Each block is processed in parallel, if the blocks are large enough they themselves +/// are split into parallel sub-blocks. +/// +/// # Arguments +/// - `mat`: Mutable matrix whose height is a power of two. +/// - `twiddles`: Precomputed twiddle factors for this layer. +#[inline] +fn dif_layer_par(mat: &mut RowMajorMatrixViewMut, twiddles: &[F]) { + debug_assert!( + mat.height() % twiddles.len() == 0, + "Matrix height must be divisible by the number of twiddles" + ); + let size = mat.values.len(); + let num_blocks = twiddles.len(); + + let outer_block_size = size / num_blocks; + let half_outer_block_size = outer_block_size / 2; + + mat.values + .par_chunks_exact_mut(outer_block_size) + .enumerate() + .for_each(|(ind, block)| { + // Split each block vertically into top (hi) and bottom (lo) halves + let (hi_chunk, lo_chunk) = block.split_at_mut(half_outer_block_size); + + // If num_blocks is small, we probably are not using all available threads. + let num_threads = current_num_threads(); + let inner_block_size = size / (2 * num_blocks).max(num_threads); + + hi_chunk + .par_chunks_mut(inner_block_size) + .zip(lo_chunk.par_chunks_mut(inner_block_size)) + .for_each(|(hi_chunk, lo_chunk)| { + if ind == 0 { + // The first pair doesn't require a twiddle factor + TwiddleFreeButterfly.apply_to_rows(hi_chunk, lo_chunk); + } else { + // Apply DIF butterfly using the twiddle factor at index `ind - 1` + DifButterfly(twiddles[ind]).apply_to_rows(hi_chunk, lo_chunk); + } + }); + }); +} + +/// Splits the matrix into chunks of size `chunk_size` and performs +/// the remaining layers of the FFT in parallel on each chunk. +/// +/// This avoids passing data between threads, which can be expensive. +#[inline] +fn par_remaining_layers(mat: &mut [F], chunk_size: usize, root_table: &[Vec]) { + mat.par_chunks_exact_mut(chunk_size) + .enumerate() + .for_each(|(index, chunk)| { + for (layer, twiddles) in root_table.iter().rev().enumerate() { + let num_twiddles_per_block = 1 << layer; + let start = index * num_twiddles_per_block; + let twiddle_range = start..(start + num_twiddles_per_block); + dit_layer(chunk, &twiddles[twiddle_range]); + } + }); +} + +/// Splits the matrix into chunks of size `chunk_size` and performs +/// the initial layers of the iFFT in parallel on each chunk. +/// +/// This avoids passing data between threads, which can be expensive. +/// +/// Basically identical to [par_remaining_layers] but in reverse. +#[inline] +fn par_initial_layers( + mat: &mut [F], + chunk_size: usize, + root_table: &[Vec], + log_height: usize, +) { + let num_rounds = root_table.len(); + let height_inv = F::ONE.div_2exp_u64(log_height as u64); + mat.par_chunks_exact_mut(chunk_size) + .enumerate() + .for_each(|(index, chunk)| { + // Divide all elements by the height of the matrix. + scale_slice_in_place_single_core(chunk, height_inv); + + for (layer, twiddles) in root_table.iter().enumerate() { + let num_twiddles_per_block = 1 << (num_rounds - layer - 1); + let start = index * num_twiddles_per_block; + let twiddle_range = start..(start + num_twiddles_per_block); + dif_layer(chunk, &twiddles[twiddle_range]); + } + }); +} + +fn par_middle_layers( + in_mat: &mut RowMajorMatrixViewMut, + out_mat: &mut RowMajorMatrixViewMut, + num_par_rows: usize, + root_table: &[Vec], + inv_root_table: &[Vec], + added_bits: usize, + shift: F, +) { + debug_assert_eq!(in_mat.width(), out_mat.width()); + debug_assert_eq!(in_mat.height() << added_bits, out_mat.height()); + + let width = in_mat.width(); + let height = in_mat.height(); + let num_rounds = root_table.len(); + let in_chunk_size = num_par_rows * width; + let out_chunk_size = in_chunk_size << added_bits; + + let log_height = log2_strict_usize(height); + let inv_height = F::ONE.div_2exp_u64(log_height as u64); + + let scaling_packed: Vec = F::Packing::packed_shifted_powers(shift, inv_height) + .take(height.div_ceil(F::Packing::WIDTH)) + .collect::>(); + let mut scaling: Vec = unsafe { flatten_to_base(scaling_packed) }; + scaling.truncate(height); + reverse_slice_index_bits(&mut scaling); + + in_mat + .values + .par_chunks_exact_mut(in_chunk_size) + .zip(out_mat.values.par_chunks_exact_mut(out_chunk_size)) + .zip(scaling.par_chunks_exact_mut(num_par_rows)) + .enumerate() + .for_each(|(index, ((in_chunk, out_chunk), scaling))| { + for (layer, twiddles) in inv_root_table.iter().rev().enumerate() { + let num_twiddles_per_block = 1 << layer; + let start = index * num_twiddles_per_block; + let twiddle_range = start..(start + num_twiddles_per_block); + dit_layer(in_chunk, &twiddles[twiddle_range]); + } + + // Copy the values to the output matrix and scale appropriately. + in_chunk + .chunks_exact(width) + .zip(scaling) + .zip(out_chunk.chunks_exact_mut(width << added_bits)) + .for_each(|((in_row, scale), out_row)| { + out_row + .iter_mut() + .zip(in_row.iter()) + .for_each(|(out_val, in_val)| { + *out_val = *in_val * *scale; + }); + }); + + for (layer, twiddles) in root_table.iter().enumerate() { + let num_twiddles_per_block = 1 << (num_rounds - layer - 1); + let start = index * num_twiddles_per_block; + let twiddle_range = start..(start + num_twiddles_per_block); + // While + if layer < added_bits { + dif_layer_zeros(out_chunk, &twiddles[twiddle_range], added_bits - layer - 1); + } else { + dif_layer(out_chunk, &twiddles[twiddle_range]); + } + } + }); +} + +/// Applies one layer of the Radix-2 DIT FFT butterfly network on a single core. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. +/// +/// # Arguments +/// - `vec`: Mutable vector whose height is a power of two. +/// - `twiddles`: Precomputed twiddle factors for this layer. +#[inline] +fn dit_layer(vec: &mut [F], twiddles: &[F]) { + debug_assert_eq!( + vec.len() % twiddles.len(), + 0, + "Vector length must be divisible by the number of twiddles" + ); + let size = vec.len(); + let num_blocks = twiddles.len(); + + let block_size = size / num_blocks; + let half_block_size = block_size / 2; + + vec.chunks_exact_mut(block_size) + .zip(twiddles) + .for_each(|(block, &twiddle)| { + // Split each block vertically into top (hi) and bottom (lo) halves + let (hi_chunk, lo_chunk) = block.split_at_mut(half_block_size); + + // Apply DIT butterfly + DitButterfly(twiddle).apply_to_rows(hi_chunk, lo_chunk); + }); +} + +/// Applies one layer of the Radix-2 DIF FFT butterfly network on a single core. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. +/// +/// # Arguments +/// - `vec`: Mutable vector whose height is a power of two. +/// - `twiddles`: Precomputed twiddle factors for this layer. +#[inline] +fn dif_layer(vec: &mut [F], twiddles: &[F]) { + debug_assert_eq!( + vec.len() % twiddles.len(), + 0, + "Vector length must be divisible by the number of twiddles" + ); + let size = vec.len(); + let num_blocks = twiddles.len(); + + let block_size = size / num_blocks; + let half_block_size = block_size / 2; + + vec.chunks_exact_mut(block_size) + .zip(twiddles) + .for_each(|(block, &twiddle)| { + // Split each block vertically into top (hi) and bottom (lo) halves + let (hi_chunk, lo_chunk) = block.split_at_mut(half_block_size); + + // Apply DIF butterfly + DifButterfly(twiddle).apply_to_rows(hi_chunk, lo_chunk); + }); +} + +/// Applies two layers of the Radix-2 DIT FFT butterfly network making use of parallelization. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. Advantage of doing two layers at once is it reduces the amount of +/// data transferred between threads. +/// +/// # Arguments +/// - `mat`: Mutable matrix whose height is a power of two. +/// - `twiddles_0`: Precomputed twiddle factors for the first layer. +/// - `twiddles_1`: Precomputed twiddle factors for the second layer. +#[inline] +fn dit_layer_par_double( + mat: &mut RowMajorMatrixViewMut, + twiddles_0: &[F], + twiddles_1: &[F], +) { + debug_assert!( + mat.height() % twiddles_0.len() == 0, + "Matrix height must be divisible by the number of twiddles" + ); + let size = mat.values.len(); + let num_blocks = twiddles_0.len(); + + let outer_block_size = size / num_blocks; + let quarter_outer_block_size = outer_block_size / 4; + + // Estimate the optimal size of the inner chunks so that all data fits in L1 cache. + // Note that 4 inner chunks are processed in each parallel thread so we divide by 4. + let inner_chunk_size = + (workload_size::().next_power_of_two() / 4).min(quarter_outer_block_size); + + mat.values + .par_chunks_exact_mut(outer_block_size) + .enumerate() + .for_each(|(ind, block)| { + // Split each block into four quarters. Each quarter will be further split into + // sub-chunks processed in parallel. + let chunk_par_iters_0 = block + .chunks_exact_mut(quarter_outer_block_size) + .map(|chunk| chunk.par_chunks_mut(inner_chunk_size)) + .collect::>(); + let chunk_par_iters_1 = zip_par_iter_vec(chunk_par_iters_0); + chunk_par_iters_1.into_iter().tuples().for_each(|(hi, lo)| { + hi.zip(lo) + .for_each(|((hi_hi_chunk, hi_lo_chunk), (lo_hi_chunk, lo_lo_chunk))| { + // Do 2 layers of the DIT FFT butterfly network at once. + if ind == 0 { + // Layer 0: + TwiddleFreeButterfly.apply_to_rows(hi_hi_chunk, lo_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_lo_chunk, lo_lo_chunk); + + // Layer 1: + TwiddleFreeButterfly.apply_to_rows(hi_hi_chunk, hi_lo_chunk); + DitButterfly(twiddles_1[1]).apply_to_rows(lo_hi_chunk, lo_lo_chunk); + } else { + // Layer 0: + DitButterfly(twiddles_0[ind]).apply_to_rows(hi_hi_chunk, lo_hi_chunk); + DitButterfly(twiddles_0[ind]).apply_to_rows(hi_lo_chunk, lo_lo_chunk); + + // Layer 1: + DitButterfly(twiddles_1[2 * ind]) + .apply_to_rows(hi_hi_chunk, hi_lo_chunk); + DitButterfly(twiddles_1[2 * ind + 1]) + .apply_to_rows(lo_hi_chunk, lo_lo_chunk); + } + }); + }); + }); +} + +/// Applies three layers of the Radix-2 DIT FFT butterfly network making use of parallelization. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. Advantage of doing three layers at once is it reduces the amount of +/// data transferred between threads. +/// +/// # Arguments +/// - `mat`: Mutable matrix whose height is a power of two. +/// - `twiddles_0`: Precomputed twiddle factors for the first layer. +/// - `twiddles_1`: Precomputed twiddle factors for the second layer. +/// - `twiddles_2`: Precomputed twiddle factors for the third layer. +#[inline] +fn dit_layer_par_triple( + mat: &mut RowMajorMatrixViewMut, + twiddles_0: &[F], + twiddles_1: &[F], + twiddles_2: &[F], +) { + debug_assert!( + mat.height() % twiddles_0.len() == 0, + "Matrix height must be divisible by the number of twiddles" + ); + let size = mat.values.len(); + let num_blocks = twiddles_0.len(); + + let outer_block_size = size / num_blocks; + let eighth_outer_block_size = outer_block_size / 8; + + // Estimate the optimal size of the inner chunks so that all data fits in L1 cache. + // Note that 8 inner chunks are processed in each parallel thread so we divide by 8. + let inner_chunk_size = + (workload_size::().next_power_of_two() / 8).min(eighth_outer_block_size); + + mat.values + .par_chunks_exact_mut(outer_block_size) + .enumerate() + .for_each(|(ind, block)| { + // Split each block into eight equal parts. Each part will be further split into + // sub-chunks processed in parallel. + let chunk_par_iters_0 = block + .chunks_exact_mut(eighth_outer_block_size) + .map(|chunk| chunk.par_chunks_mut(inner_chunk_size)) + .collect::>(); + let chunk_par_iters_1 = zip_par_iter_vec(chunk_par_iters_0); + let chunk_par_iters_2 = zip_par_iter_vec(chunk_par_iters_1); + chunk_par_iters_2.into_iter().tuples().for_each(|(hi, lo)| { + hi.zip(lo).for_each( + |( + ((hi_hi_hi_chunk, hi_hi_lo_chunk), (hi_lo_hi_chunk, hi_lo_lo_chunk)), + ((lo_hi_hi_chunk, lo_hi_lo_chunk), (lo_lo_hi_chunk, lo_lo_lo_chunk)), + )| { + // Do 3 layers of the DIT FFT butterfly network at once. + if ind == 0 { + // Layer 0: + TwiddleFreeButterfly.apply_to_rows(hi_hi_hi_chunk, lo_hi_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_hi_lo_chunk, lo_hi_lo_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_lo_hi_chunk, lo_lo_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_lo_lo_chunk, lo_lo_lo_chunk); + + // Layer 1: + TwiddleFreeButterfly.apply_to_rows(hi_hi_hi_chunk, hi_lo_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_hi_lo_chunk, hi_lo_lo_chunk); + DitButterfly(twiddles_1[1]) + .apply_to_rows(lo_hi_hi_chunk, lo_lo_hi_chunk); + DitButterfly(twiddles_1[1]) + .apply_to_rows(lo_hi_lo_chunk, lo_lo_lo_chunk); + + // Layer 2: + TwiddleFreeButterfly.apply_to_rows(hi_hi_hi_chunk, hi_hi_lo_chunk); + DitButterfly(twiddles_2[1]) + .apply_to_rows(hi_lo_hi_chunk, hi_lo_lo_chunk); + DitButterfly(twiddles_2[2]) + .apply_to_rows(lo_hi_hi_chunk, lo_hi_lo_chunk); + DitButterfly(twiddles_2[3]) + .apply_to_rows(lo_lo_hi_chunk, lo_lo_lo_chunk); + } else { + // Layer 0: + DitButterfly(twiddles_0[ind]) + .apply_to_rows(hi_hi_hi_chunk, lo_hi_hi_chunk); + DitButterfly(twiddles_0[ind]) + .apply_to_rows(hi_hi_lo_chunk, lo_hi_lo_chunk); + DitButterfly(twiddles_0[ind]) + .apply_to_rows(hi_lo_hi_chunk, lo_lo_hi_chunk); + DitButterfly(twiddles_0[ind]) + .apply_to_rows(hi_lo_lo_chunk, lo_lo_lo_chunk); + + // Layer 1: + DitButterfly(twiddles_1[2 * ind]) + .apply_to_rows(hi_hi_hi_chunk, hi_lo_hi_chunk); + DitButterfly(twiddles_1[2 * ind]) + .apply_to_rows(hi_hi_lo_chunk, hi_lo_lo_chunk); + DitButterfly(twiddles_1[2 * ind + 1]) + .apply_to_rows(lo_hi_hi_chunk, lo_lo_hi_chunk); + DitButterfly(twiddles_1[2 * ind + 1]) + .apply_to_rows(lo_hi_lo_chunk, lo_lo_lo_chunk); + + // Layer 2: + DitButterfly(twiddles_2[4 * ind]) + .apply_to_rows(hi_hi_hi_chunk, hi_hi_lo_chunk); + DitButterfly(twiddles_2[4 * ind + 1]) + .apply_to_rows(hi_lo_hi_chunk, hi_lo_lo_chunk); + DitButterfly(twiddles_2[4 * ind + 2]) + .apply_to_rows(lo_hi_hi_chunk, lo_hi_lo_chunk); + DitButterfly(twiddles_2[4 * ind + 3]) + .apply_to_rows(lo_lo_hi_chunk, lo_lo_lo_chunk); + } + }, + ) + }); + }); +} + +/// Applies two layers of the Radix-2 DIF FFT butterfly network making use of parallelization. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. Advantage of doing two layers at once is it reduces the amount of +/// data transferred between threads. +/// +/// # Arguments +/// - `mat`: Mutable matrix whose height is a power of two. +/// - `twiddles_0`: Precomputed twiddle factors for the first layer. +/// - `twiddles_1`: Precomputed twiddle factors for the second layer. +#[inline] +fn dif_layer_par_double( + mat: &mut RowMajorMatrixViewMut, + twiddles_0: &[F], + twiddles_1: &[F], +) { + debug_assert!( + mat.height() % twiddles_1.len() == 0, + "Matrix height must be divisible by the number of twiddles" + ); + let size = mat.values.len(); + let num_blocks = twiddles_1.len(); + + let outer_block_size = size / num_blocks; + let quarter_outer_block_size = outer_block_size / 4; + + // Estimate the optimal size of the inner chunks so that all data fits in L1 cache. + // Note that 4 inner chunks are processed in each parallel thread so we divide by 4. + let inner_chunk_size = + (workload_size::().next_power_of_two() / 4).min(quarter_outer_block_size); + + mat.values + .par_chunks_exact_mut(outer_block_size) + .enumerate() + .for_each(|(ind, block)| { + // Split each block into four quarters. Each quarter will be further split into + // sub-chunks processed in parallel. + let chunk_par_iters_0 = block + .chunks_exact_mut(quarter_outer_block_size) + .map(|chunk| chunk.par_chunks_mut(inner_chunk_size)) + .collect::>(); + let chunk_par_iters_1 = zip_par_iter_vec(chunk_par_iters_0); + chunk_par_iters_1.into_iter().tuples().for_each(|(hi, lo)| { + hi.zip(lo) + .for_each(|((hi_hi_chunk, hi_lo_chunk), (lo_hi_chunk, lo_lo_chunk))| { + // Do 2 layers of the DIF FFT butterfly network at once. + if ind == 0 { + // Layer 0: + TwiddleFreeButterfly.apply_to_rows(hi_hi_chunk, hi_lo_chunk); + DifButterfly(twiddles_0[1]).apply_to_rows(lo_hi_chunk, lo_lo_chunk); + + // Layer 1: + TwiddleFreeButterfly.apply_to_rows(hi_hi_chunk, lo_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_lo_chunk, lo_lo_chunk); + } else { + // Layer 0: + DifButterfly(twiddles_0[2 * ind]) + .apply_to_rows(hi_hi_chunk, hi_lo_chunk); + DifButterfly(twiddles_0[2 * ind + 1]) + .apply_to_rows(lo_hi_chunk, lo_lo_chunk); + + // Layer 1: + DifButterfly(twiddles_1[ind]).apply_to_rows(hi_hi_chunk, lo_hi_chunk); + DifButterfly(twiddles_1[ind]).apply_to_rows(hi_lo_chunk, lo_lo_chunk); + } + }); + }); + }); +} + +/// Applies three layers of the Radix-2 DIF FFT butterfly network making use of parallelization. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. Advantage of doing three layers at once is it reduces the amount of +/// data transferred between threads. +/// +/// # Arguments +/// - `mat`: Mutable matrix whose height is a power of two. +/// - `twiddles_0`: Precomputed twiddle factors for the first layer. +/// - `twiddles_1`: Precomputed twiddle factors for the second layer. +/// - `twiddles_2`: Precomputed twiddle factors for the third layer. +#[inline] +fn dif_layer_par_triple( + mat: &mut RowMajorMatrixViewMut, + twiddles_0: &[F], + twiddles_1: &[F], + twiddles_2: &[F], +) { + debug_assert!( + mat.height() % twiddles_2.len() == 0, + "Matrix height must be divisible by the number of twiddles" + ); + let size = mat.values.len(); + let num_blocks = twiddles_2.len(); + + let outer_block_size = size / num_blocks; + let eighth_outer_block_size = outer_block_size / 8; + + // Estimate the optimal size of the inner chunks so that all data fits in L1 cache. + // Note that 8 inner chunks are processed in each parallel thread so we divide by 8. + let inner_chunk_size = + (workload_size::().next_power_of_two() / 8).min(eighth_outer_block_size); + + mat.values + .par_chunks_exact_mut(outer_block_size) + .enumerate() + .for_each(|(ind, block)| { + // Split each block into eight equal parts. Each part will be further split into + // sub-chunks processed in parallel. + let chunk_par_iters_0 = block + .chunks_exact_mut(eighth_outer_block_size) + .map(|chunk| chunk.par_chunks_mut(inner_chunk_size)) + .collect::>(); + let chunk_par_iters_1 = zip_par_iter_vec(chunk_par_iters_0); + let chunk_par_iters_2 = zip_par_iter_vec(chunk_par_iters_1); + chunk_par_iters_2.into_iter().tuples().for_each(|(hi, lo)| { + hi.zip(lo).for_each( + |( + ((hi_hi_hi_chunk, hi_hi_lo_chunk), (hi_lo_hi_chunk, hi_lo_lo_chunk)), + ((lo_hi_hi_chunk, lo_hi_lo_chunk), (lo_lo_hi_chunk, lo_lo_lo_chunk)), + )| { + // Do 3 layers of the DIF FFT butterfly network at once. + if ind == 0 { + // Layer 0: + TwiddleFreeButterfly.apply_to_rows(hi_hi_hi_chunk, hi_hi_lo_chunk); + DifButterfly(twiddles_0[1]) + .apply_to_rows(hi_lo_hi_chunk, hi_lo_lo_chunk); + DifButterfly(twiddles_0[2]) + .apply_to_rows(lo_hi_hi_chunk, lo_hi_lo_chunk); + DifButterfly(twiddles_0[3]) + .apply_to_rows(lo_lo_hi_chunk, lo_lo_lo_chunk); + + // Layer 1: + TwiddleFreeButterfly.apply_to_rows(hi_hi_hi_chunk, hi_lo_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_hi_lo_chunk, hi_lo_lo_chunk); + DifButterfly(twiddles_1[1]) + .apply_to_rows(lo_hi_hi_chunk, lo_lo_hi_chunk); + DifButterfly(twiddles_1[1]) + .apply_to_rows(lo_hi_lo_chunk, lo_lo_lo_chunk); + + // Layer 2: + TwiddleFreeButterfly.apply_to_rows(hi_hi_hi_chunk, lo_hi_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_hi_lo_chunk, lo_hi_lo_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_lo_hi_chunk, lo_lo_hi_chunk); + TwiddleFreeButterfly.apply_to_rows(hi_lo_lo_chunk, lo_lo_lo_chunk); + } else { + // Layer 0: + DifButterfly(twiddles_0[4 * ind]) + .apply_to_rows(hi_hi_hi_chunk, hi_hi_lo_chunk); + DifButterfly(twiddles_0[4 * ind + 1]) + .apply_to_rows(hi_lo_hi_chunk, hi_lo_lo_chunk); + DifButterfly(twiddles_0[4 * ind + 2]) + .apply_to_rows(lo_hi_hi_chunk, lo_hi_lo_chunk); + DifButterfly(twiddles_0[4 * ind + 3]) + .apply_to_rows(lo_lo_hi_chunk, lo_lo_lo_chunk); + + // Layer 1: + DifButterfly(twiddles_1[2 * ind]) + .apply_to_rows(hi_hi_hi_chunk, hi_lo_hi_chunk); + DifButterfly(twiddles_1[2 * ind]) + .apply_to_rows(hi_hi_lo_chunk, hi_lo_lo_chunk); + DifButterfly(twiddles_1[2 * ind + 1]) + .apply_to_rows(lo_hi_hi_chunk, lo_lo_hi_chunk); + DifButterfly(twiddles_1[2 * ind + 1]) + .apply_to_rows(lo_hi_lo_chunk, lo_lo_lo_chunk); + + // Layer 2: + DifButterfly(twiddles_2[ind]) + .apply_to_rows(hi_hi_hi_chunk, lo_hi_hi_chunk); + DifButterfly(twiddles_2[ind]) + .apply_to_rows(hi_hi_lo_chunk, lo_hi_lo_chunk); + DifButterfly(twiddles_2[ind]) + .apply_to_rows(hi_lo_hi_chunk, lo_lo_hi_chunk); + DifButterfly(twiddles_2[ind]) + .apply_to_rows(hi_lo_lo_chunk, lo_lo_lo_chunk); + } + }, + ) + }); + }); +} + +/// Applies one layer of the Radix-2 DIF FFT butterfly network on a single core to +/// a recently zero-padded matrix. +/// +/// Splits the matrix into blocks of rows and performs in-place butterfly operations +/// on each block. +/// +/// Assume `added_bits = 2`. Then the rows of our matrix look like: +/// ```text +/// [R0, 0, 0, 0, R1, 0, 0, 0, ...] +/// ``` +/// Thus the first two butterfly layers can be implemented more simply as they map the matrix to: +/// ```text +/// After Layer 0: [R0, T00 * R0, 0, 0, R1, T01 * R1, 0, 0, ...] +/// After Layer 1: [R0, T00 * R0, T10 * R0, T10 * T00 * R0, R1, T01 * R1, T11 * R1, T11 * T01 * R1, ...]. +/// ``` +/// +/// # Arguments +/// - `vec`: Mutable vector whose height is a power of two. +/// - `twiddles`: Precomputed twiddle factors for this layer. +/// - `skip`: `(1 << skip) - 1` is the number of entirely zero +/// blocks between each non zero block. +#[inline] +fn dif_layer_zeros(vec: &mut [F], twiddles: &[F], skip: usize) { + debug_assert_eq!( + vec.len() % twiddles.len(), + 0, + "Vector length must be divisible by the number of twiddles" + ); + let size = vec.len(); + let num_blocks = twiddles.len(); + + let block_size = size / num_blocks; + let half_block_size = block_size / 2; + + vec.chunks_exact_mut(block_size) + .zip(twiddles) + .step_by(1 << skip) // Skip the zero blocks + .for_each(|(block, &twiddle)| { + // Split each block vertically into top (hi) and bottom (lo) halves + let (hi_chunk, lo_chunk) = block.split_at_mut(half_block_size); + + // Apply DIF butterfly making use of the fact that `lo_chunk` is zero. + DifButterflyZeros(twiddle).apply_to_rows(hi_chunk, lo_chunk); + }); +} + +/// Estimates the optimal workload size for `T` to fit in L1 cache. +/// +/// Approximates the size of the L1 cache by 32 KB. Used to determine the number of +/// chunks to process in parallel. +#[must_use] +const fn workload_size() -> usize { + const L1_CACHE_SIZE: usize = 1 << 15; // 32 KB + L1_CACHE_SIZE / size_of::() +} + +/// Estimates the optimal number of rows of a `RowMajorMatrix` to take in each parallel chunk. +/// +/// Designed to ensure that ` * estimate_num_rows_par() * width` is roughly the size of the L1 cache. +/// +/// Assumes that height is a power of two and always outputs a power of two. +#[must_use] +fn estimate_num_rows_in_l1(height: usize, width: usize) -> usize { + (workload_size::() / width) + .next_power_of_two() + .min(height) // Ensure we don't exceed the height of the matrix. +} + +/// Given a vector of parallel iterators, zip all pairs together. +/// +/// This lets us simulate the izip!() macro but for our possibly parallel iterators. +/// +/// This function assumes that the input vector has an even number of elements. If +/// it is given an odd number of elements, the last element will be ignored. +#[inline] +fn zip_par_iter_vec( + in_vec: Vec, +) -> Vec> { + in_vec + .into_iter() + .tuples() + .map(|(hi, lo)| hi.zip(lo)) + .collect::>() +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/traits.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/traits.rs new file mode 100644 index 00000000..8390a06e --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/traits.rs @@ -0,0 +1,498 @@ +use alloc::vec::Vec; + +use p3_field::{BasedVectorSpace, TwoAdicField}; +use p3_matrix::Matrix; +use p3_matrix::bitrev::BitReversibleMatrix; +use p3_matrix::dense::RowMajorMatrix; +use p3_matrix::util::swap_rows; + +use crate::util::{coset_shift_cols, divide_by_height}; + +/// This trait gives an interface for computing discrete fourier transforms (DFT's) and their inverses over +/// cosets of two-adic subgroups of a field `F`. It also contains combined methods which allow you to take the +/// evaluation vector of a polynomial on a coset `gH` and extend it to a coset `g'K` for some possibly larger +/// subgroup `K` and different shift `g'`. +/// +/// It supports polynomials with evaluations/coefficients valued in either `F` or `A` where `A` +/// is a vector space over `F` with specified basis. This latter case makes use of the fact that the DFT +/// is linear meaning we can decompose an `A` valued polynomial into a collection of `F` valued polynomials, +/// apply the DFT to each of them, and then recombine. When `A` is an extension field, this approach +/// is much faster than using a `TwoAdicSubgroupDft` implementation directly. +/// +/// Most implementations of this trait are optimised for the batch case where the input +/// is a matrix and we is a want to perform the same operation on every column. Note that +/// depending on the width and height of the matrix (as well as whether or not you are using the +/// parallel feature) different implementation may be faster. Hence depending on your use case +/// you may want to be using `Radix2Dit, Radix2DitParallel, RecursiveDft` or `Radix2Bowers`. +pub trait TwoAdicSubgroupDft: Clone + Default { + /// The matrix type used to store the result of a batched DFT operation. + /// + /// This type represents a matrix of field elements, used to hold the evaluations + /// of multiple polynomials over a two-adic subgroup or its coset. + /// It is always owned and supports efficient access and transformation + /// patterns used in FFT-based algorithms. + /// + /// Most implementations use `RowMajorMatrix` or a wrapper like + /// `BitReversedMatrixView>` to allow in-place bit-reversed access. + type Evaluations: BitReversibleMatrix + 'static; + + /// Compute the discrete Fourier transform (DFT) of `vec`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as coefficients of a polynomial, compute the evaluations + /// of that polynomial on the subgroup `H`. + fn dft(&self, vec: Vec) -> Vec { + self.dft_batch(RowMajorMatrix::new_col(vec)) + .to_row_major_matrix() + .values + } + + /// Compute the discrete Fourier transform (DFT) of each column in `mat`. + /// This is the only method an implementer needs to define, all other + /// methods can be derived from this one. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the coefficients of a polynomial, compute the + /// evaluations of those polynomials on the subgroup `H`. + fn dft_batch(&self, mat: RowMajorMatrix) -> Self::Evaluations; + + /// Compute the "coset DFT" of `vec`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as coefficients of a polynomial, compute the evaluations + /// of that polynomial on the coset `shift * H`. + fn coset_dft(&self, vec: Vec, shift: F) -> Vec { + self.coset_dft_batch(RowMajorMatrix::new_col(vec), shift) + .to_row_major_matrix() + .values + } + + /// Compute the "coset DFT" of each column in `mat`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the coefficients of a polynomial, compute the + /// evaluations of those polynomials on the coset `shift * H`. + fn coset_dft_batch(&self, mut mat: RowMajorMatrix, shift: F) -> Self::Evaluations { + // Observe that + // y_i = \sum_j c_j (s g^i)^j + // = \sum_j (c_j s^j) (g^i)^j + // which has the structure of an ordinary DFT, except each coefficient `c_j` is first replaced + // by `c_j s^j`. + coset_shift_cols(&mut mat, shift); + self.dft_batch(mat) + } + + /// Compute the inverse DFT of `vec`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as the evaluations of a polynomial on `H`, compute the + /// coefficients of that polynomial. + fn idft(&self, vec: Vec) -> Vec { + self.idft_batch(RowMajorMatrix::new_col(vec)).values + } + + /// Compute the inverse DFT of each column in `mat`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the evaluations of a polynomial on `H`, + /// compute the coefficients of those polynomials. + fn idft_batch(&self, mat: RowMajorMatrix) -> RowMajorMatrix { + let mut dft = self.dft_batch(mat).to_row_major_matrix(); + let h = dft.height(); + + divide_by_height(&mut dft); + + for row in 1..h / 2 { + swap_rows(&mut dft, row, h - row); + } + + dft + } + + /// Compute the "coset iDFT" of `vec`. This is the inverse operation of "coset DFT". + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as the evaluations of a polynomial on `shift * H`, + /// compute the coefficients of this polynomial. + fn coset_idft(&self, vec: Vec, shift: F) -> Vec { + self.coset_idft_batch(RowMajorMatrix::new_col(vec), shift) + .values + } + + /// Compute the "coset iDFT" of each column in `mat`. This is the inverse operation + /// of "coset DFT". + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the evaluations of a polynomial on `shift * H`, + /// compute the coefficients of those polynomials. + fn coset_idft_batch(&self, mut mat: RowMajorMatrix, shift: F) -> RowMajorMatrix { + // Let `f(x)` denote the polynomial we want. Then, if we reinterpret the columns + // as being over the subgroup `H`, this is equivalent to switching our polynomial + // to `g(x) = f(sx)`. + // The output of the iDFT is the coefficients of `g` so to get the coefficients of + // `f` we need to scale the `i`'th coefficient by `s^{-i}`. + mat = self.idft_batch(mat); + coset_shift_cols(&mut mat, shift.inverse()); + mat + } + + /// Compute the low-degree extension of `vec` onto a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()` + /// and `vec.len() << added_bits`, respectively. + /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of that polynomial on the subgroup `K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating columns of `mat` as evaluations + /// over a coset `gH` and then computing the evaluations of those polynomials + /// on the coset `gK`. + fn lde(&self, vec: Vec, added_bits: usize) -> Vec { + self.lde_batch(RowMajorMatrix::new_col(vec), added_bits) + .to_row_major_matrix() + .values + } + + /// Compute the low-degree extension of each column in `mat` onto a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()` + /// and `mat.height() << added_bits`, respectively. + /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of those polynomials on the subgroup `K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating columns of `mat` as evaluations + /// over a coset `gH` and then computing the evaluations of those polynomials + /// on the coset `gK`. + fn lde_batch(&self, mat: RowMajorMatrix, added_bits: usize) -> Self::Evaluations { + // This is a better default as several implementations have a custom implementation + // of `coset_lde_batch` and often the fact that the shift is `ONE` won't give any + // performance improvements anyway. + self.coset_lde_batch(mat, added_bits, F::ONE) + } + + /// Compute the low-degree extension of of `vec` onto a coset of a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()` + /// and `vec.len() << added_bits`, respectively. + /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of that polynomial on the coset `shift * K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating `vec` as the evaluations of a polynomial + /// over a coset `gH` and then computing the evaluations of that polynomial + /// on the coset `g'K` where `g' = g * shift`. + fn coset_lde(&self, vec: Vec, added_bits: usize, shift: F) -> Vec { + self.coset_lde_batch(RowMajorMatrix::new_col(vec), added_bits, shift) + .to_row_major_matrix() + .values + } + + /// Compute the low-degree extension of each column in `mat` onto a coset of a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()` + /// and `mat.height() << added_bits`, respectively. + /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of those polynomials on the coset `shift * K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating columns of `mat` as evaluations + /// over a coset `gH` and then computing the evaluations of those polynomials + /// on the coset `g'K` where `g' = g * shift`. + fn coset_lde_batch( + &self, + mat: RowMajorMatrix, + added_bits: usize, + shift: F, + ) -> Self::Evaluations { + // To briefly explain the additional interpretation, start with the evaluations of the polynomial + // `f(x)` over `gH`. If we reinterpret the evaluations as being over the subgroup `H`, this is equivalent to + // switching our polynomial to `f1(x) = f(g x)`. The output of the iDFT will be the coefficients of + // `f1`. Next, when we scale by shift, we are effectively switching to the polynomial + // `f2(x) = f1(shift * x) = f(shift * g x)`. Applying the DFT to this, we get the evaluations of `f2` over + // `K` which is the evaluations of `f1` over `shift * K` which is the evaluations of `f` over `g * shift * K`. + let mut coeffs = self.idft_batch(mat); + // PANICS: possible panic if the new resized length overflows + coeffs.values.resize( + coeffs + .values + .len() + .checked_shl(added_bits.try_into().unwrap()) + .unwrap(), + F::ZERO, + ); + self.coset_dft_batch(coeffs, shift) + } + + /// Compute the discrete Fourier transform (DFT) of `vec`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as coefficients of a polynomial, compute the evaluations + /// of that polynomial on the subgroup `H`. + fn dft_algebra + Clone + Send + Sync>(&self, vec: Vec) -> Vec { + self.dft_algebra_batch(RowMajorMatrix::new_col(vec)).values + } + + /// Compute the discrete Fourier transform (DFT) of each column in `mat`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the coefficients of a polynomial, compute the + /// evaluations of those polynomials on the subgroup `H`. + fn dft_algebra_batch + Clone + Send + Sync>( + &self, + mat: RowMajorMatrix, + ) -> RowMajorMatrix { + let init_width = mat.width(); + let base_mat = + RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION); + let base_dft_output = self.dft_batch(base_mat).to_row_major_matrix(); + RowMajorMatrix::new( + V::reconstitute_from_base(base_dft_output.values), + init_width, + ) + } + + /// Compute the "coset DFT" of `vec`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as coefficients of a polynomial, compute the evaluations + /// of that polynomial on the coset `shift * H`. + fn coset_dft_algebra + Clone + Send + Sync>( + &self, + vec: Vec, + shift: F, + ) -> Vec { + self.coset_dft_algebra_batch(RowMajorMatrix::new_col(vec), shift) + .to_row_major_matrix() + .values + } + + /// Compute the "coset DFT" of each column in `mat`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the coefficients of a polynomial, compute the + /// evaluations of those polynomials on the coset `shift * H`. + fn coset_dft_algebra_batch + Clone + Send + Sync>( + &self, + mat: RowMajorMatrix, + shift: F, + ) -> RowMajorMatrix { + let init_width = mat.width(); + let base_mat = + RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION); + let base_dft_output = self.coset_dft_batch(base_mat, shift).to_row_major_matrix(); + RowMajorMatrix::new( + V::reconstitute_from_base(base_dft_output.values), + init_width, + ) + } + + /// Compute the inverse DFT of `vec`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as the evaluations of a polynomial on `H`, compute the + /// coefficients of that polynomial. + fn idft_algebra + Clone + Send + Sync>(&self, vec: Vec) -> Vec { + self.idft_algebra_batch(RowMajorMatrix::new_col(vec)).values + } + + /// Compute the inverse DFT of each column in `mat`. + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the evaluations of a polynomial on `H`, + /// compute the coefficients of those polynomials. + fn idft_algebra_batch + Clone + Send + Sync>( + &self, + mat: RowMajorMatrix, + ) -> RowMajorMatrix { + let init_width = mat.width(); + let base_mat = + RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION); + let base_dft_output = self.idft_batch(base_mat); + RowMajorMatrix::new( + V::reconstitute_from_base(base_dft_output.values), + init_width, + ) + } + + /// Compute the "coset iDFT" of `vec`. This is the inverse operation of "coset DFT". + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`. + /// Treating `vec` as the evaluations of a polynomial on `shift * H`, + /// compute the coefficients of this polynomial. + fn coset_idft_algebra + Clone + Send + Sync>( + &self, + vec: Vec, + shift: F, + ) -> Vec { + self.coset_idft_algebra_batch(RowMajorMatrix::new_col(vec), shift) + .values + } + + /// Compute the "coset iDFT" of each column in `mat`. This is the inverse operation + /// of "coset DFT". + /// + /// #### Mathematical Description + /// + /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`. + /// Treating each column of `mat` as the evaluations of a polynomial on `shift * H`, + /// compute the coefficients of those polynomials. + fn coset_idft_algebra_batch + Clone + Send + Sync>( + &self, + mat: RowMajorMatrix, + shift: F, + ) -> RowMajorMatrix { + let init_width = mat.width(); + let base_mat = + RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION); + let base_dft_output = self.coset_idft_batch(base_mat, shift); + RowMajorMatrix::new( + V::reconstitute_from_base(base_dft_output.values), + init_width, + ) + } + + /// Compute the low-degree extension of `vec` onto a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()` + /// and `vec.len() << added_bits`, respectively. + /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of that polynomial on the subgroup `K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating columns of `mat` as evaluations + /// over a coset `gH` and then computing the evaluations of those polynomials + /// on the coset `gK`. + fn lde_algebra + Clone + Send + Sync>( + &self, + vec: Vec, + added_bits: usize, + ) -> Vec { + self.lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits) + .to_row_major_matrix() + .values + } + + /// Compute the low-degree extension of each column in `mat` onto a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()` + /// and `mat.height() << added_bits`, respectively. + /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of those polynomials on the subgroup `K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating columns of `mat` as evaluations + /// over a coset `gH` and then computing the evaluations of those polynomials + /// on the coset `gK`. + fn lde_algebra_batch + Clone + Send + Sync>( + &self, + mat: RowMajorMatrix, + added_bits: usize, + ) -> RowMajorMatrix { + let init_width = mat.width(); + let base_mat = + RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION); + let base_dft_output = self.lde_batch(base_mat, added_bits).to_row_major_matrix(); + RowMajorMatrix::new( + V::reconstitute_from_base(base_dft_output.values), + init_width, + ) + } + + /// Compute the low-degree extension of of `vec` onto a coset of a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()` + /// and `vec.len() << added_bits`, respectively. + /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of that polynomial on the coset `shift * K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating `vec` as the evaluations of a polynomial + /// over a coset `gH` and then computing the evaluations of that polynomial + /// on the coset `g'K` where `g' = g * shift`. + fn coset_lde_algebra + Clone + Send + Sync>( + &self, + vec: Vec, + added_bits: usize, + shift: F, + ) -> Vec { + self.coset_lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits, shift) + .to_row_major_matrix() + .values + } + + /// Compute the low-degree extension of each column in `mat` onto a coset of a larger subgroup. + /// + /// #### Mathematical Description + /// + /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()` + /// and `mat.height() << added_bits`, respectively. + /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`, + /// compute the evaluations of those polynomials on the coset `shift * K`. + /// + /// There is another way to interpret this transformation which gives a larger + /// use case. We can also view it as treating columns of `mat` as evaluations + /// over a coset `gH` and then computing the evaluations of those polynomials + /// on the coset `g'K` where `g' = g * shift`. + fn coset_lde_algebra_batch + Clone + Send + Sync>( + &self, + mat: RowMajorMatrix, + added_bits: usize, + shift: F, + ) -> RowMajorMatrix { + let init_width = mat.width(); + let base_mat = + RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION); + let base_dft_output = self + .coset_lde_batch(base_mat, added_bits, shift) + .to_row_major_matrix(); + RowMajorMatrix::new( + V::reconstitute_from_base(base_dft_output.values), + init_width, + ) + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/util.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/util.rs new file mode 100644 index 00000000..a1024079 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-dft/src/util.rs @@ -0,0 +1,152 @@ +use core::borrow::BorrowMut; + +use p3_field::{Field, PrimeCharacteristicRing}; +use p3_matrix::Matrix; +use p3_matrix::dense::{DenseMatrix, DenseStorage, RowMajorMatrix}; +use p3_util::log2_strict_usize; + +/// Divide each coefficient of the given matrix by its height. +/// +/// # Panics +/// +/// Panics if the height of the matrix is not a power of two. +pub fn divide_by_height + BorrowMut<[F]>>( + mat: &mut DenseMatrix, +) { + let h = mat.height(); + let log_h = log2_strict_usize(h); + // It's cheaper to use div_2exp_u64 as this usually avoids an inversion. + // It's also cheaper to work in the PrimeSubfield whenever possible. + let h_inv_subfield = F::PrimeSubfield::ONE.div_2exp_u64(log_h as u64); + let h_inv = F::from_prime_subfield(h_inv_subfield); + mat.scale(h_inv) +} + +/// Multiply each element of row `i` of `mat` by `shift**i`. +pub(crate) fn coset_shift_cols(mat: &mut RowMajorMatrix, shift: F) { + mat.rows_mut() + .zip(shift.powers()) + .for_each(|(row, weight)| { + row.iter_mut().for_each(|coeff| { + *coeff *= weight; + }) + }); +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use p3_baby_bear::BabyBear; + use p3_matrix::dense::RowMajorMatrix; + + use super::*; + + type F = BabyBear; + + #[test] + fn test_divide_by_height_2x2() { + // Matrix: + // [ 2, 4 ] + // [ 6, 8 ] + // + // height = 2 => divide each element by 2 + let mut mat = RowMajorMatrix::new( + vec![F::from_u8(2), F::from_u8(4), F::from_u8(6), F::from_u8(8)], + 2, + ); + + divide_by_height(&mut mat); + + // Compute: [2, 4, 6, 8] * 1/2 = [1, 2, 3, 4] + let expected = vec![F::from_u8(1), F::from_u8(2), F::from_u8(3), F::from_u8(4)]; + + assert_eq!(mat.values, expected); + } + + #[test] + fn test_divide_by_height_1x4() { + // Matrix: + // [ 10, 20, 30, 40 ] + // height = 1 => no division (1⁻¹ = 1), matrix should remain unchanged + let mut mat = RowMajorMatrix::new_row(vec![ + F::from_u8(10), + F::from_u8(20), + F::from_u8(30), + F::from_u8(40), + ]); + + divide_by_height(&mut mat); + + let expected = vec![ + F::from_u8(10), + F::from_u8(20), + F::from_u8(30), + F::from_u8(40), + ]; + + assert_eq!(mat.values, expected); + } + + #[test] + #[should_panic] + fn test_divide_by_height_non_power_of_two_height_should_panic() { + // Matrix of height = 3 is not a power of two → should panic + let mut mat = RowMajorMatrix::new(vec![F::from_u8(1), F::from_u8(2), F::from_u8(3)], 1); + + divide_by_height(&mut mat); + } + + #[test] + fn test_coset_shift_cols_3x2_shift_2() { + // Input matrix: + // [ 1, 2 ] + // [ 3, 4 ] + // [ 5, 6 ] + // + // shift = 2 + // Row 0: shift^0 = 1 → [1 * 1, 2 * 1] = [1, 2] + // Row 1: shift^1 = 2 → [3 * 2, 4 * 2] = [6, 8] + // Row 2: shift^2 = 4 → [5 * 4, 6 * 4] = [20, 24] + + let mut mat = RowMajorMatrix::new( + vec![ + F::from_u8(1), + F::from_u8(2), + F::from_u8(3), + F::from_u8(4), + F::from_u8(5), + F::from_u8(6), + ], + 2, + ); + + coset_shift_cols(&mut mat, F::from_u8(2)); + + let expected = vec![ + F::from_u8(1), + F::from_u8(2), + F::from_u8(6), + F::from_u8(8), + F::from_u8(20), + F::from_u8(24), + ]; + + assert_eq!(mat.values, expected); + } + + #[test] + fn test_coset_shift_cols_identity_shift() { + // shift = 1 → all weights = 1 → matrix should remain unchanged + let mut mat = RowMajorMatrix::new( + vec![F::from_u8(7), F::from_u8(8), F::from_u8(9), F::from_u8(10)], + 2, + ); + + coset_shift_cols(&mut mat, F::from_u8(1)); + + let expected = vec![F::from_u8(7), F::from_u8(8), F::from_u8(9), F::from_u8(10)]; + + assert_eq!(mat.values, expected); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/.cargo-ok b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/.cargo-ok new file mode 100644 index 00000000..5f8b7958 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/.cargo_vcs_info.json b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/.cargo_vcs_info.json new file mode 100644 index 00000000..d6a2615a --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/.cargo_vcs_info.json @@ -0,0 +1,7 @@ +{ + "git": { + "sha1": "3148b7148a19bb56dedb55291ae9b06223904e88", + "dirty": true + }, + "path_in_vcs": "field" +} \ No newline at end of file diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.lock b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.lock new file mode 100644 index 00000000..2ba0fef6 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.lock @@ -0,0 +1,191 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[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 = "p3-field" +version = "0.3.0" +dependencies = [ + "itertools", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f765046b763d046728b3246b690f81dfa7ccd7523b7a1582c74f616fbce6a0" + +[[package]] +name = "p3-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfee67245d9ce78a15176728da2280032f0a84b5819a39a953e7ec03cfd9bd7" +dependencies = [ + "serde", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.toml b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.toml new file mode 100644 index 00000000..9ff27d04 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.toml @@ -0,0 +1,79 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +name = "p3-field" +version = "0.3.0" +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Plonky3 is a toolkit for implementing polynomial IOPs (PIOPs), such as PLONK and STARKs." +homepage = "https://github.com/0xPolygonZero/Plonky3" +readme = false +keywords = [ + "cryptography", + "SNARK", + "PLONK", + "FRI", + "plonky3", +] +categories = ["cryptography::cryptocurrencies"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/0xPolygonZero/Plonky3" +resolver = "2" + +[lib] +name = "p3_field" +path = "src/lib.rs" + +[[test]] +name = "coset_tests" +path = "tests/coset_tests.rs" + +[[test]] +name = "helpers_test" +path = "tests/helpers_test.rs" + +[dependencies.itertools] +version = "0.14.0" +features = ["use_alloc"] +default-features = false + +[dependencies.num-bigint] +version = "0.4.3" +default-features = false + +[dependencies.p3-maybe-rayon] +version = "0.3.0" + +[dependencies.p3-util] +version = "0.3.0" + +[dependencies.paste] +version = "1.0.15" + +[dependencies.rand] +version = "0.9.0" +features = ["small_rng"] +default-features = false + +[dependencies.serde] +version = "1.0" +features = ["derive"] +default-features = false +["attributes"] +default-features = false + +[dev-dependencies] diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.toml.orig b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.toml.orig new file mode 100644 index 00000000..19a5387b --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/Cargo.toml.orig @@ -0,0 +1,26 @@ +[package] +name = "p3-field" +# TODO: Replace this generic plonky3 description with one specific to this crate... +description.workspace = true +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +keywords.workspace = true +categories.workspace = true + +[dependencies] +p3-maybe-rayon.workspace = true +p3-util.workspace = true + +itertools.workspace = true +num-bigint.workspace = true +paste.workspace = true +rand.workspace = true +serde = { workspace = true, features = ["derive"] } +tracing.workspace = true + +[dev-dependencies] +p3-baby-bear = { path = "../baby-bear" } +p3-goldilocks = { path = "../goldilocks" } diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/array.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/array.rs new file mode 100644 index 00000000..a8bcd9ba --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/array.rs @@ -0,0 +1,213 @@ +use core::array; +use core::iter::{Product, Sum}; +use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; + +use crate::batch_inverse::batch_multiplicative_inverse_general; +use crate::{Algebra, Field, PackedValue, PrimeCharacteristicRing}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(transparent)] // Needed to make `transmute`s safe. +pub struct FieldArray(pub [F; N]); + +impl FieldArray { + pub(crate) fn inverse(&self) -> Self { + let mut result = Self::default(); + batch_multiplicative_inverse_general(&self.0, &mut result.0, |x| x.inverse()); + result + } +} + +impl Default for FieldArray { + fn default() -> Self { + Self::ZERO + } +} + +impl From for FieldArray { + fn from(val: F) -> Self { + [val; N].into() + } +} + +impl From<[F; N]> for FieldArray { + fn from(arr: [F; N]) -> Self { + Self(arr) + } +} + +impl PrimeCharacteristicRing for FieldArray { + type PrimeSubfield = F::PrimeSubfield; + + const ZERO: Self = Self([F::ZERO; N]); + const ONE: Self = Self([F::ONE; N]); + const TWO: Self = Self([F::TWO; N]); + const NEG_ONE: Self = Self([F::NEG_ONE; N]); + + #[inline] + fn from_prime_subfield(f: Self::PrimeSubfield) -> Self { + F::from_prime_subfield(f).into() + } +} + +impl Algebra for FieldArray {} + +unsafe impl PackedValue for FieldArray { + type Value = F; + + const WIDTH: usize = N; + + fn from_slice(slice: &[Self::Value]) -> &Self { + assert_eq!(slice.len(), Self::WIDTH); + unsafe { &*slice.as_ptr().cast() } + } + + fn from_slice_mut(slice: &mut [Self::Value]) -> &mut Self { + assert_eq!(slice.len(), Self::WIDTH); + unsafe { &mut *slice.as_mut_ptr().cast() } + } + + fn from_fn(f: Fn) -> Self + where + Fn: FnMut(usize) -> Self::Value, + { + Self(array::from_fn(f)) + } + + fn as_slice(&self) -> &[Self::Value] { + &self.0 + } + + fn as_slice_mut(&mut self) -> &mut [Self::Value] { + &mut self.0 + } +} + +impl Add for FieldArray { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self::Output { + array::from_fn(|i| self.0[i] + rhs.0[i]).into() + } +} + +impl Add for FieldArray { + type Output = Self; + + #[inline] + fn add(self, rhs: F) -> Self::Output { + self.0.map(|x| x + rhs).into() + } +} + +impl AddAssign for FieldArray { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.0.iter_mut().zip(rhs.0).for_each(|(x, y)| *x += y); + } +} + +impl AddAssign for FieldArray { + #[inline] + fn add_assign(&mut self, rhs: F) { + self.0.iter_mut().for_each(|x| *x += rhs); + } +} + +impl Sub for FieldArray { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + array::from_fn(|i| self.0[i] - rhs.0[i]).into() + } +} + +impl Sub for FieldArray { + type Output = Self; + + #[inline] + fn sub(self, rhs: F) -> Self::Output { + self.0.map(|x| x - rhs).into() + } +} + +impl SubAssign for FieldArray { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.0.iter_mut().zip(rhs.0).for_each(|(x, y)| *x -= y); + } +} + +impl SubAssign for FieldArray { + #[inline] + fn sub_assign(&mut self, rhs: F) { + self.0.iter_mut().for_each(|x| *x -= rhs); + } +} + +impl Neg for FieldArray { + type Output = Self; + + #[inline] + fn neg(self) -> Self::Output { + self.0.map(|x| -x).into() + } +} + +impl Mul for FieldArray { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + array::from_fn(|i| self.0[i] * rhs.0[i]).into() + } +} + +impl Mul for FieldArray { + type Output = Self; + + #[inline] + fn mul(self, rhs: F) -> Self::Output { + self.0.map(|x| x * rhs).into() + } +} + +impl MulAssign for FieldArray { + #[inline] + fn mul_assign(&mut self, rhs: Self) { + self.0.iter_mut().zip(rhs.0).for_each(|(x, y)| *x *= y); + } +} + +impl MulAssign for FieldArray { + #[inline] + fn mul_assign(&mut self, rhs: F) { + self.0.iter_mut().for_each(|x| *x *= rhs); + } +} + +impl Div for FieldArray { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline] + fn div(self, rhs: F) -> Self::Output { + let rhs_inv = rhs.inverse(); + self * rhs_inv + } +} + +impl Sum for FieldArray { + #[inline] + fn sum>(iter: I) -> Self { + iter.reduce(|lhs, rhs| lhs + rhs).unwrap_or(Self::ZERO) + } +} + +impl Product for FieldArray { + #[inline] + fn product>(iter: I) -> Self { + iter.reduce(|lhs, rhs| lhs * rhs).unwrap_or(Self::ONE) + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/batch_inverse.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/batch_inverse.rs new file mode 100644 index 00000000..f0860656 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/batch_inverse.rs @@ -0,0 +1,80 @@ +use alloc::vec::Vec; + +use p3_maybe_rayon::prelude::*; + +use crate::field::Field; +use crate::{FieldArray, PackedValue, PrimeCharacteristicRing}; + +/// Batch multiplicative inverses with Montgomery's trick +/// This is Montgomery's trick. At a high level, we invert the product of the given field +/// elements, then derive the individual inverses from that via multiplication. +/// +/// The usual Montgomery trick involves calculating an array of cumulative products, +/// resulting in a long dependency chain. To increase instruction-level parallelism, we +/// compute WIDTH separate cumulative product arrays that only meet at the end. +/// +/// # Panics +/// This will panic if any of the inputs is zero. +pub fn batch_multiplicative_inverse(x: &[F]) -> Vec { + // How many elements to invert in one thread. + const CHUNK_SIZE: usize = 1024; + + let n = x.len(); + let mut result = F::zero_vec(n); + + x.par_chunks(CHUNK_SIZE) + .zip(result.par_chunks_mut(CHUNK_SIZE)) + .for_each(|(x, result)| { + batch_multiplicative_inverse_helper(x, result); + }); + + result +} + +/// Like `batch_multiplicative_inverse`, but writes the result to the given output buffer. +fn batch_multiplicative_inverse_helper(x: &[F], result: &mut [F]) { + // Higher WIDTH increases instruction-level parallelism, but too high a value will cause us + // to run out of registers. + const WIDTH: usize = 4; + + let n = x.len(); + assert_eq!(result.len(), n); + if n % WIDTH != 0 { + // There isn't a very clean way to do this with FieldArray; for now just do it in serial. + // Another simple (though suboptimal) workaround would be to make two separate calls, one + // for the packed part and one for the remainder. + return batch_multiplicative_inverse_general(x, result, |x| x.inverse()); + } + + let x_packed = FieldArray::::pack_slice(x); + let result_packed = FieldArray::::pack_slice_mut(result); + + batch_multiplicative_inverse_general(x_packed, result_packed, |x_packed| x_packed.inverse()); +} + +/// A simple single-threaded implementation of Montgomery's trick. Since not all `PrimeCharacteristicRing`s +/// support inversion, this takes a custom inversion function. +pub(crate) fn batch_multiplicative_inverse_general(x: &[F], result: &mut [F], inv: Inv) +where + F: PrimeCharacteristicRing + Copy, + Inv: Fn(F) -> F, +{ + let n = x.len(); + assert_eq!(result.len(), n); + if n == 0 { + return; + } + + result[0] = F::ONE; + for i in 1..n { + result[i] = result[i - 1] * x[i - 1]; + } + + let product = result[n - 1] * x[n - 1]; + let mut inv = inv(product); + + for i in (0..n).rev() { + result[i] *= inv; + inv *= x[i]; + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/coset.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/coset.rs new file mode 100644 index 00000000..03d3debe --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/coset.rs @@ -0,0 +1,245 @@ +use core::iter::Take; + +use crate::{Powers, TwoAdicField}; + +/// Coset of a subgroup of the group of units of a finite field of order equal +/// to a power of two. +/// +/// # Examples +/// +/// ``` +/// # use p3_field::{ +/// TwoAdicField, +/// PrimeCharacteristicRing, +/// coset::TwoAdicMultiplicativeCoset +/// }; +/// # use itertools::Itertools; +/// # use p3_baby_bear::BabyBear; +/// # +/// type F = BabyBear; +/// let log_size = 3; +/// let shift = F::from_u64(7); +/// let mut coset = TwoAdicMultiplicativeCoset::new(shift, log_size).unwrap(); +/// let generator = coset.subgroup_generator(); +/// +/// // Coset elements can be queried by index +/// assert_eq!(coset.element(4), shift * generator.exp_u64(4)); +/// +/// // Coset elements can be iterated over in the canonical order +/// assert_eq!( +/// coset.iter().collect_vec(), +/// (0..1 << log_size).map(|i| shift * generator.exp_u64(i)).collect_vec() +/// ); +/// +/// // Cosets can be (element-wise) raised to a power of 2, either maintaining +/// // the shift and raising only the subgroup, or raising both. +/// let coset_shrunk_subgroup = coset.shrink_coset(2).unwrap(); +/// assert_eq!( +/// coset_shrunk_subgroup.subgroup_generator(), +/// coset.subgroup_generator().exp_power_of_2(2), +/// ); +/// assert_eq!( +/// coset_shrunk_subgroup.shift(), +/// coset.shift() +/// ); +/// +/// let coset_power = coset.exp_power_of_2(2).unwrap(); +/// assert_eq!( +/// coset_power.subgroup_generator(), +/// coset.subgroup_generator().exp_power_of_2(2), +/// ); +/// assert_eq!( +/// coset_power.shift(), +/// coset.shift().exp_power_of_2(2), +/// ); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct TwoAdicMultiplicativeCoset { + // Letting s = shift, and g = generator (of order 2^log_size), the coset in + // question is + // s * = {s, s * g, shift * g^2, ..., s * g^(2^log_size - 1)] + shift: F, + shift_inverse: F, + log_size: usize, +} + +impl TwoAdicMultiplicativeCoset { + /// Returns the coset `shift * `, where `generator` is a + /// canonical (i. e. fixed in the implementation of `F: TwoAdicField`) + /// generator of the unique subgroup of the units of `F` of order `2 ^ + /// log_size`. Returns `None` if `log_size > F::TWO_ADICITY` or if `shift` is zero. + /// + /// # Arguments + /// + /// - `shift`: the value by which the subgroup is (multiplicatively) + /// shifted + /// - `log_size`: the size of the subgroup (and hence of the coset) is `2 ^ + /// log_size`. This determines the subgroup uniquely. + pub fn new(shift: F, log_size: usize) -> Option { + (shift != F::ZERO && log_size <= F::TWO_ADICITY).then(|| { + let shift_inverse = shift.inverse(); + Self { + shift, + shift_inverse, + log_size, + } + }) + } + + /// Returns the generator of the subgroup of order `self.size()`. + #[inline] + pub fn subgroup_generator(&self) -> F { + F::two_adic_generator(self.log_size) + } + + /// Returns the shift of the coset. + #[inline] + pub const fn shift(&self) -> F { + self.shift + } + + /// Returns the inverse of the coset shift. + #[inline] + pub const fn shift_inverse(&self) -> F { + self.shift_inverse + } + + /// Returns the log2 of the size of the coset. + #[inline] + pub const fn log_size(&self) -> usize { + self.log_size + } + + /// Returns the size of the coset. + #[inline] + pub const fn size(&self) -> usize { + 1 << self.log_size + } + + /// Returns a new coset with its subgroup reduced by a factor of + /// `2^log_scale_factor` in size (i. e. with generator equal to the + /// `2^log_scale_factor`-th power of that of the original coset), leaving + /// the shift untouched. Note that new coset is contained in the original one. + /// Returns `None` if `log_scale_factor` is greater than `self.log_size()`. + pub fn shrink_coset(&self, log_scale_factor: usize) -> Option { + self.log_size + .checked_sub(log_scale_factor) + .map(|new_log_size| Self { + shift: self.shift, + shift_inverse: self.shift_inverse, + log_size: new_log_size, + }) + } + + /// Returns the coset `self^(2^log_scale_factor)` (i. e. with shift and + /// subgroup generator equal to the `2^log_scale_factor`-th power of the + /// original ones). Returns `None` if `log_scale_factor` is greater than `self.log_size()`. + pub fn exp_power_of_2(&self, log_scale_factor: usize) -> Option { + self.shrink_coset(log_scale_factor).map(|mut coset| { + coset.shift = self.shift.exp_power_of_2(log_scale_factor); + coset.shift_inverse = self.shift_inverse.exp_power_of_2(log_scale_factor); + coset + }) + } + + /// Returns a new coset of the same size whose shift is equal to `scale * self.shift`. + pub fn shift_by(&self, scale: F) -> Self { + let shift = self.shift * scale; + Self { + shift, + shift_inverse: shift.inverse(), + log_size: self.log_size, + } + } + + /// Returns a new coset where the shift has been set to `shift` + pub fn set_shift(&self, shift: F) -> Self { + Self { + shift, + shift_inverse: shift.inverse(), + log_size: self.log_size, + } + } + + /// Checks if the given field element is in the coset + pub fn contains(&self, element: F) -> bool { + // Note that, in a finite field F (this is not true of a general finite + // commutative ring), there is exactly one subgroup of |F^*| of order n + // for each divisor n of |F| - 1, and its elements e are uniquely + // characterised by the condition e^n = 1. + + // We check (shift^{-1} * element)^(2^log_size) = 1, which is equivalent + // to checking shift^(2^log_size) = element^(2^log_size) - this avoids + // inversion at the cost of a few squarings. The loop terminates early + // if possible. + let (mut shift, mut element) = (self.shift, element); + + for _ in 0..self.log_size { + if element == shift { + return true; + } + element = element.square(); + shift = shift.square(); + } + + element == shift + } + + /// Returns the element `shift * generator^index`, which is the `index % + /// self.size()`-th element of `self` (and, in particular, the `index`-th + /// element of `self` whenever `index` < self.size()). + #[inline] + pub fn element(&mut self, index: usize) -> F { + self.shift * self.generator_exp(index) + } + + // Internal function which computes `generator^exp`. It uses the + // square-and-multiply algorithm with the caveat that squares of the + // generator are queried from the field (which typically should have them + // stored), i. e. rather "fetch-and-multiply". + fn generator_exp(&self, exp: usize) -> F { + let mut gen_power = F::ONE; + // As `generator` satisfies `generator^{self.size()} == 1` we can replace `exp` by `exp mod self.size()`. + // As `self.size()` is a power of `2` this can be done with an `&` instead of a `%`. + let mut exp = exp & (self.size() - 1); + let mut i = self.log_size(); + + while exp > 0 { + if exp & 1 != 0 { + gen_power *= F::two_adic_generator(i); + } + exp >>= 1; + + i -= 1; + } + + gen_power + } + + /// Returns an iterator over the elements of the coset in the canonical order + /// `shift * generator^0, shift * generator^1, ..., + /// shift * generator^(2^log_size - 1)`. + pub fn iter(&self) -> Take> { + self.subgroup_generator() + .shifted_powers(self.shift) + .take(1 << self.log_size) + } +} + +impl IntoIterator for TwoAdicMultiplicativeCoset { + type Item = F; + type IntoIter = Take>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl IntoIterator for &TwoAdicMultiplicativeCoset { + type Item = F; + type IntoIter = Take>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/exponentiation.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/exponentiation.rs new file mode 100644 index 00000000..b07e50c8 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/exponentiation.rs @@ -0,0 +1,118 @@ +use crate::PrimeCharacteristicRing; + +pub(crate) const fn bits_u64(n: u64) -> usize { + (64 - n.leading_zeros()) as usize +} + +/// Compute the exponential `x -> x^1717986917` using a custom addition chain. +/// +/// This map computes the fifth root of `x` if `x` is a member of the field `Mersenne31`. +/// This follows from the computation: `5 * 1717986917 = 4*(2^31 - 2) + 1 = 1 mod p - 1`. +pub fn exp_1717986917(val: R) -> R { + // Note the binary expansion: 1717986917 = 1100110011001100110011001100101_2 + // This uses 30 Squares + 7 Multiplications => 37 Operations total. + // Suspect it's possible to improve this with enough effort. For example 1717986918 takes only 4 Multiplications. + let p1 = val; + let p10 = p1.square(); + let p11 = p10.clone() * p1; + let p101 = p10 * p11.clone(); + let p110000 = p11.exp_power_of_2(4); + let p110011 = p110000 * p11.clone(); + let p11001100000000 = p110011.exp_power_of_2(8); + let p11001100110011 = p11001100000000.clone() * p110011; + let p1100110000000000000000 = p11001100000000.exp_power_of_2(8); + let p1100110011001100110011 = p1100110000000000000000 * p11001100110011; + let p11001100110011001100110000 = p1100110011001100110011.exp_power_of_2(4); + let p11001100110011001100110011 = p11001100110011001100110000 * p11; + let p1100110011001100110011001100000 = p11001100110011001100110011.exp_power_of_2(5); + p1100110011001100110011001100000 * p101 +} + +/// Compute the exponential `x -> x^1420470955` using a custom addition chain. +/// +/// This map computes the third root of `x` if `x` is a member of the field `KoalaBear`. +/// This follows from the computation: `3 * 1420470955 = 2*(2^31 - 2^24) + 1 = 1 mod (p - 1)`. +pub fn exp_1420470955(val: R) -> R { + // Note the binary expansion: 1420470955 = 1010100101010101010101010101011_2 + // This uses 29 Squares + 7 Multiplications => 36 Operations total. + // Suspect it's possible to improve this with enough effort. + let p1 = val; + let p100 = p1.exp_power_of_2(2); + let p101 = p100.clone() * p1.clone(); + let p10000 = p100.exp_power_of_2(2); + let p10101 = p10000 * p101; + let p10101000000 = p10101.exp_power_of_2(6); + let p10101010101 = p10101000000.clone() * p10101.clone(); + let p101010010101 = p10101000000 * p10101010101.clone(); + let p101010010101000000000000 = p101010010101.exp_power_of_2(12); + let p101010010101010101010101 = p101010010101000000000000 * p10101010101; + let p101010010101010101010101000000 = p101010010101010101010101.exp_power_of_2(6); + let p101010010101010101010101010101 = p101010010101010101010101000000 * p10101; + let p1010100101010101010101010101010 = p101010010101010101010101010101.square(); + p1010100101010101010101010101010 * p1 +} + +/// Compute the exponential `x -> x^1725656503` using a custom addition chain. +/// +/// This map computes the seventh root of `x` if `x` is a member of the field `BabyBear`. +/// This follows from the computation: `7 * 1725656503 = 6*(2^31 - 2^27) + 1 = 1 mod (p - 1)`. +pub fn exp_1725656503(val: R) -> R { + // Note the binary expansion: 1725656503 = 1100110110110110110110110110111_2 + // This uses 29 Squares + 8 Multiplications => 37 Operations total. + // Suspect it's possible to improve this with enough effort. + let p1 = val; + let p10 = p1.square(); + let p11 = p10 * p1.clone(); + let p110 = p11.square(); + let p111 = p110.clone() * p1; + let p11000 = p110.exp_power_of_2(2); + let p11011 = p11000.clone() * p11; + let p11000000 = p11000.exp_power_of_2(3); + let p11011011 = p11000000.clone() * p11011; + let p110011011 = p11011011.clone() * p11000000; + let p110011011000000000 = p110011011.exp_power_of_2(9); + let p110011011011011011 = p110011011000000000 * p11011011.clone(); + let p110011011011011011000000000 = p110011011011011011.exp_power_of_2(9); + let p110011011011011011011011011 = p110011011011011011000000000 * p11011011; + let p1100110110110110110110110110000 = p110011011011011011011011011.exp_power_of_2(4); + p1100110110110110110110110110000 * p111 +} + +/// Compute the exponential `x -> x^10540996611094048183` using a custom addition chain. +/// +/// This map computes the seventh root of `x` if `x` is a member of the field `Goldilocks`. +/// This follows from the computation: `7 * 10540996611094048183 = 4*(2^64 - 2**32) + 1 = 1 mod (p - 1)`. +pub fn exp_10540996611094048183(val: R) -> R { + // Note the binary expansion: 10540996611094048183 = 1001001001001001001001001001000110110110110110110110110110110111_2. + // This uses 63 Squares + 8 Multiplications => 71 Operations total. + // Suspect it's possible to improve this a little with enough effort. + let p1 = val; + let p10 = p1.square(); + let p11 = p10.clone() * p1; + let p100 = p10.square(); + let p111 = p100.clone() * p11.clone(); + let p100000000000000000000000000000000 = p100.exp_power_of_2(30); + let p100000000000000000000000000000011 = p100000000000000000000000000000000 * p11; + let p100000000000000000000000000000011000 = + p100000000000000000000000000000011.exp_power_of_2(3); + let p100100000000000000000000000000011011 = + p100000000000000000000000000000011000 * p100000000000000000000000000000011; + let p100100000000000000000000000000011011000000 = + p100100000000000000000000000000011011.exp_power_of_2(6); + let p100100100100000000000000000000011011011011 = + p100100000000000000000000000000011011000000 * p100100000000000000000000000000011011.clone(); + let p100100100100000000000000000000011011011011000000000000 = + p100100100100000000000000000000011011011011.exp_power_of_2(12); + let p100100100100100100100100000000011011011011011011011011 = + p100100100100000000000000000000011011011011000000000000 + * p100100100100000000000000000000011011011011; + let p100100100100100100100100000000011011011011011011011011000000 = + p100100100100100100100100000000011011011011011011011011.exp_power_of_2(6); + let p100100100100100100100100100100011011011011011011011011011011 = + p100100100100100100100100000000011011011011011011011011000000 + * p100100000000000000000000000000011011; + let p1001001001001001001001001001000110110110110110110110110110110000 = + p100100100100100100100100100100011011011011011011011011011011.exp_power_of_2(4); + + p1001001001001001001001001001000110110110110110110110110110110000 * p111 +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/binomial_extension.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/binomial_extension.rs new file mode 100644 index 00000000..e56a0bb1 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/binomial_extension.rs @@ -0,0 +1,1050 @@ +use alloc::format; +use alloc::string::ToString; +use alloc::vec::Vec; +use core::array; +use core::fmt::{self, Debug, Display, Formatter}; +use core::iter::{Product, Sum}; +use core::marker::PhantomData; +use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use itertools::Itertools; +use num_bigint::BigUint; +use p3_util::{as_base_slice, as_base_slice_mut, flatten_to_base, reconstitute_from_base}; +use rand::distr::StandardUniform; +use rand::prelude::Distribution; +use serde::{Deserialize, Serialize}; + +use super::{HasFrobenius, HasTwoAdicBinomialExtension, PackedBinomialExtensionField}; +use crate::extension::BinomiallyExtendable; +use crate::field::Field; +use crate::{ + Algebra, BasedVectorSpace, ExtensionField, Packable, PrimeCharacteristicRing, + RawDataSerializable, TwoAdicField, field_to_array, +}; + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord)] +#[repr(transparent)] // Needed to make various casts safe. +pub struct BinomialExtensionField { + #[serde( + with = "p3_util::array_serialization", + bound(serialize = "A: Serialize", deserialize = "A: Deserialize<'de>") + )] + pub(crate) value: [A; D], + _phantom: PhantomData, +} + +impl BinomialExtensionField { + pub(crate) const fn new(value: [A; D]) -> Self { + Self { + value, + _phantom: PhantomData, + } + } +} + +impl, const D: usize> Default for BinomialExtensionField { + fn default() -> Self { + Self::new(array::from_fn(|_| A::ZERO)) + } +} + +impl, const D: usize> From for BinomialExtensionField { + fn from(x: A) -> Self { + Self::new(field_to_array(x)) + } +} + +impl, const D: usize> Packable for BinomialExtensionField {} + +impl, A: Algebra, const D: usize> BasedVectorSpace + for BinomialExtensionField +{ + const DIMENSION: usize = D; + + #[inline] + fn as_basis_coefficients_slice(&self) -> &[A] { + &self.value + } + + #[inline] + fn from_basis_coefficients_fn A>(f: Fn) -> Self { + Self::new(array::from_fn(f)) + } + + #[inline] + fn from_basis_coefficients_iter>(mut iter: I) -> Option { + (iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) // The unwrap is safe as we just checked the length of iter. + } + + #[inline] + fn flatten_to_base(vec: Vec) -> Vec { + unsafe { + // Safety: + // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[A; D]` + flatten_to_base::(vec) + } + } + + #[inline] + fn reconstitute_from_base(vec: Vec) -> Vec { + unsafe { + // Safety: + // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[A; D]` + reconstitute_from_base::(vec) + } + } +} + +impl, const D: usize> ExtensionField + for BinomialExtensionField +{ + type ExtensionPacking = PackedBinomialExtensionField; + + #[inline] + fn is_in_basefield(&self) -> bool { + self.value[1..].iter().all(F::is_zero) + } + + #[inline] + fn as_base(&self) -> Option { + >::is_in_basefield(self).then(|| self.value[0]) + } +} + +impl, const D: usize> HasFrobenius for BinomialExtensionField { + /// FrobeniusField automorphisms: x -> x^n, where n is the order of BaseField. + #[inline] + fn frobenius(&self) -> Self { + // Slightly faster than self.repeated_frobenius(1) + let mut res = Self::ZERO; + for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() { + res.value[i] = self.value[i] * z; + } + + res + } + + /// Repeated Frobenius automorphisms: x -> x^(n^count). + /// + /// Follows precomputation suggestion in Section 11.3.3 of the + /// Handbook of Elliptic and Hyperelliptic Curve Cryptography. + #[inline] + fn repeated_frobenius(&self, count: usize) -> Self { + if count == 0 { + return *self; + } else if count >= D { + // x |-> x^(n^D) is the identity, so x^(n^count) == + // x^(n^(count % D)) + return self.repeated_frobenius(count % D); + } + + // z0 = DTH_ROOT^count = W^(k * count) where k = floor((n-1)/D) + let z0 = F::DTH_ROOT.exp_u64(count as u64); + + let mut res = Self::ZERO; + for (i, z) in z0.powers().take(D).enumerate() { + res.value[i] = self.value[i] * z; + } + + res + } + + /// Compute the inverse of a given element making use of the Frobenius automorphism. + /// + /// Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. + #[inline] + fn frobenius_inv(&self) -> Self { + // Writing 'a' for self and `q` for the order of the base field, our goal is to compute `a^{-1}`. + // + // Note that we can write `-1 = (q^{D - 1} + ... + q) - (q^{D - 1} + ... + q + 1)`. + // This is a useful decomposition as powers of q can be efficiently computed using the frobenius + // automorphism and `Norm(a) = a^{(q^{D - 1} + ... + q + 1)}` is guaranteed to lie in the base field. + // This means that `Norm(a)^{-1}` can be computed using base field operations. + // + // Hence this implementation first computes `ProdConj(a) = a^{q^{D - 1} + ... + q}` using frobenius automorphisms. + // From this, it computes `Norm(a) = a * ProdConj(a)` and returns `ProdConj(a) * Norm(a)^{-1} = a^{-1}`. + + // This loop requires a linear number of multiplications and Frobenius automorphisms. + // If D is known, it is possible to do this in a logarithmic number. See quintic_inv + // for an example of this. + let mut prod_conj = self.frobenius(); + for _ in 2..D { + prod_conj = (prod_conj * *self).frobenius(); + } + + // norm = a * prod_conj is in the base field, so only compute that + // coefficient rather than the full product. + let a = self.value; + let b = prod_conj.value; + let mut w_coeff = F::ZERO; + // This should really be a dot product but + // const generics doesn't let this happen: + // b.reverse(); + // let mut g = F::dot_product::<{D - 1}>(a[1..].try_into().unwrap(), b[..D - 1].try_into().unwrap()); + for i in 1..D { + w_coeff += a[i] * b[D - i]; + } + let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]); + debug_assert_eq!(Self::from(norm), *self * prod_conj); + + prod_conj * norm.inverse() + } +} + +impl PrimeCharacteristicRing for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type PrimeSubfield = ::PrimeSubfield; + + const ZERO: Self = Self::new([A::ZERO; D]); + + const ONE: Self = Self::new(field_to_array(A::ONE)); + + const TWO: Self = Self::new(field_to_array(A::TWO)); + + const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE)); + + #[inline] + fn from_prime_subfield(f: Self::PrimeSubfield) -> Self { + ::from_prime_subfield(f).into() + } + + #[inline(always)] + fn square(&self) -> Self { + let mut res = Self::default(); + let w = F::W; + match D { + 2 => { + let a = &self.value; + let a1_w = a[1].clone() * F::W; + res.value[0] = A::dot_product(a[..].try_into().unwrap(), &[a[0].clone(), a1_w]); + res.value[1] = a[0].clone() * a[1].double(); + } + 3 => cubic_square(&self.value, &mut res.value), + 4 => quartic_square(&self.value, &mut res.value, w), + 5 => quintic_square(&self.value, &mut res.value, w), + _ => binomial_mul::(&self.value, &self.value, &mut res.value, w), + } + res + } + + #[inline] + fn mul_2exp_u64(&self, exp: u64) -> Self { + Self::new(self.value.clone().map(|x| x.mul_2exp_u64(exp))) + } + + #[inline] + fn zero_vec(len: usize) -> Vec { + // SAFETY: this is a repr(transparent) wrapper around an array. + unsafe { reconstitute_from_base(F::zero_vec(len * D)) } + } +} + +impl, const D: usize> Algebra for BinomialExtensionField {} + +impl, const D: usize> RawDataSerializable + for BinomialExtensionField +{ + const NUM_BYTES: usize = F::NUM_BYTES * D; + + #[inline] + fn into_bytes(self) -> impl IntoIterator { + self.value.into_iter().flat_map(|x| x.into_bytes()) + } + + #[inline] + fn into_byte_stream(input: impl IntoIterator) -> impl IntoIterator { + F::into_byte_stream(input.into_iter().flat_map(|x| x.value)) + } + + #[inline] + fn into_u32_stream(input: impl IntoIterator) -> impl IntoIterator { + F::into_u32_stream(input.into_iter().flat_map(|x| x.value)) + } + + #[inline] + fn into_u64_stream(input: impl IntoIterator) -> impl IntoIterator { + F::into_u64_stream(input.into_iter().flat_map(|x| x.value)) + } + + #[inline] + fn into_parallel_byte_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + F::into_parallel_byte_streams( + input + .into_iter() + .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))), + ) + } + + #[inline] + fn into_parallel_u32_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + F::into_parallel_u32_streams( + input + .into_iter() + .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))), + ) + } + + #[inline] + fn into_parallel_u64_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + F::into_parallel_u64_streams( + input + .into_iter() + .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))), + ) + } +} + +impl, const D: usize> Field for BinomialExtensionField { + type Packing = Self; + + const GENERATOR: Self = Self::new(F::EXT_GENERATOR); + + fn try_inverse(&self) -> Option { + if self.is_zero() { + return None; + } + + let mut res = Self::default(); + + match D { + 2 => quadratic_inv(&self.value, &mut res.value, F::W), + 3 => cubic_inv(&self.value, &mut res.value, F::W), + 4 => quartic_inv(&self.value, &mut res.value, F::W), + 5 => res = quintic_inv(self), + _ => res = self.frobenius_inv(), + } + + Some(res) + } + + #[inline] + fn halve(&self) -> Self { + Self::new(self.value.map(|x| x.halve())) + } + + #[inline] + fn div_2exp_u64(&self, exp: u64) -> Self { + Self::new(self.value.map(|x| x.div_2exp_u64(exp))) + } + + #[inline] + fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) { + // By construction, Self is repr(transparent) over [F; D]. + // Additionally, addition is F-linear. Hence we can cast + // everything to F and use F's add_slices. + unsafe { + let base_slice_1 = as_base_slice_mut(slice_1); + let base_slice_2 = as_base_slice(slice_2); + + F::add_slices(base_slice_1, base_slice_2); + } + } + + #[inline] + fn order() -> BigUint { + F::order().pow(D as u32) + } +} + +impl Display for BinomialExtensionField +where + F: BinomiallyExtendable, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if self.is_zero() { + write!(f, "0") + } else { + let str = self + .value + .iter() + .enumerate() + .filter(|(_, x)| !x.is_zero()) + .map(|(i, x)| match (i, x.is_one()) { + (0, _) => format!("{x}"), + (1, true) => "X".to_string(), + (1, false) => format!("{x} X"), + (_, true) => format!("X^{i}"), + (_, false) => format!("{x} X^{i}"), + }) + .join(" + "); + write!(f, "{}", str) + } + } +} + +impl Neg for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type Output = Self; + + #[inline] + fn neg(self) -> Self { + Self::new(self.value.map(A::neg)) + } +} + +impl Add for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + let value = vector_add(&self.value, &rhs.value); + Self::new(value) + } +} + +impl Add for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type Output = Self; + + #[inline] + fn add(mut self, rhs: A) -> Self { + self.value[0] += rhs; + self + } +} + +impl AddAssign for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn add_assign(&mut self, rhs: Self) { + for i in 0..D { + self.value[i] += rhs.value[i].clone(); + } + } +} + +impl AddAssign for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn add_assign(&mut self, rhs: A) { + self.value[0] += rhs; + } +} + +impl Sum for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn sum>(iter: I) -> Self { + iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO) + } +} + +impl Sub for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + let value = vector_sub(&self.value, &rhs.value); + Self::new(value) + } +} + +impl Sub for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: A) -> Self { + let mut res = self.value; + res[0] -= rhs; + Self::new(res) + } +} + +impl SubAssign for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn sub_assign(&mut self, rhs: Self) { + for i in 0..D { + self.value[i] -= rhs.value[i].clone(); + } + } +} + +impl SubAssign for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn sub_assign(&mut self, rhs: A) { + self.value[0] -= rhs; + } +} + +impl Mul for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + let a = self.value; + let b = rhs.value; + let mut res = Self::default(); + let w = F::W; + + binomial_mul::(&a, &b, &mut res.value, w); + + res + } +} + +impl Mul for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + type Output = Self; + + #[inline] + fn mul(self, rhs: A) -> Self { + Self::new(self.value.map(|x| x * rhs.clone())) + } +} + +impl MulAssign for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn mul_assign(&mut self, rhs: Self) { + *self = self.clone() * rhs; + } +} + +impl MulAssign for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn mul_assign(&mut self, rhs: A) { + *self = self.clone() * rhs; + } +} + +impl Product for BinomialExtensionField +where + F: BinomiallyExtendable, + A: Algebra, +{ + #[inline] + fn product>(iter: I) -> Self { + iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE) + } +} + +impl Div for BinomialExtensionField +where + F: BinomiallyExtendable, +{ + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + #[inline] + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse() + } +} + +impl DivAssign for BinomialExtensionField +where + F: BinomiallyExtendable, +{ + #[inline] + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs; + } +} + +impl, const D: usize> Distribution> + for StandardUniform +where + Self: Distribution, +{ + #[inline] + fn sample(&self, rng: &mut R) -> BinomialExtensionField { + BinomialExtensionField::new(array::from_fn(|_| self.sample(rng))) + } +} + +impl, const D: usize> TwoAdicField + for BinomialExtensionField +{ + const TWO_ADICITY: usize = F::EXT_TWO_ADICITY; + + #[inline] + fn two_adic_generator(bits: usize) -> Self { + Self::new(F::ext_two_adic_generator(bits)) + } +} + +/// Add two vectors element wise. +#[inline] +pub(crate) fn vector_add< + R: PrimeCharacteristicRing + Add, + R2: Clone, + const D: usize, +>( + a: &[R; D], + b: &[R2; D], +) -> [R; D] { + array::from_fn(|i| a[i].clone() + b[i].clone()) +} + +/// Subtract two vectors element wise. +#[inline] +pub(crate) fn vector_sub< + R: PrimeCharacteristicRing + Sub, + R2: Clone, + const D: usize, +>( + a: &[R; D], + b: &[R2; D], +) -> [R; D] { + array::from_fn(|i| a[i].clone() - b[i].clone()) +} + +/// Multiply two vectors representing elements in a binomial extension. +#[inline] +pub(super) fn binomial_mul< + F: Field, + R: Algebra + Algebra, + R2: Algebra, + const D: usize, +>( + a: &[R; D], + b: &[R2; D], + res: &mut [R; D], + w: F, +) { + match D { + 2 => quadratic_mul(a, b, res, w), + 3 => cubic_mul(a, b, res, w), + 4 => quartic_mul(a, b, res, w), + 5 => quintic_mul(a, b, res, w), + _ => + { + #[allow(clippy::needless_range_loop)] + for i in 0..D { + for j in 0..D { + if i + j >= D { + res[i + j - D] += a[i].clone() * w * b[j].clone(); + } else { + res[i + j] += a[i].clone() * b[j].clone(); + } + } + } + } + } +} + +/// Optimized multiplication for quadratic extension field. +/// +/// Makes use of the in built field dot product code. This is optimized for the case that +/// R is a prime field or its packing. +/// +/// ```text +/// A = a0 + a1·X +/// B = b0 + b1·X +/// ``` +/// Where `X` satisfies `X² = w`. Then the product is: +/// ```text +/// A·B = a0·b0 + a1·b1·w + (a0·b1 + a1·b0)·X +/// ``` +#[inline] +fn quadratic_mul(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F) +where + F: Field, + R: Algebra + Algebra, + R2: Algebra, +{ + let b1_w = b[1].clone() * w; + + // Compute a0·b0 + a1·b1·w + res[0] = R::dot_product( + a[..].try_into().unwrap(), + &[b[0].clone().into(), b1_w.into()], + ); + + // Compute a0·b1 + a1·b0 + res[1] = R::dot_product( + &[a[0].clone(), a[1].clone()], + &[b[1].clone().into(), b[0].clone().into()], + ); +} + +///Section 11.3.6b in Handbook of Elliptic and Hyperelliptic Curve Cryptography. +#[inline] +fn quadratic_inv(a: &[F; D], res: &mut [F; D], w: F) { + assert_eq!(D, 2); + let neg_a1 = -a[1]; + let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse(); + res[0] = a[0] * scalar; + res[1] = neg_a1 * scalar; +} + +/// Section 11.3.6b in Handbook of Elliptic and Hyperelliptic Curve Cryptography. +#[inline] +fn cubic_inv(a: &[F; D], res: &mut [F; D], w: F) { + assert_eq!(D, 3); + let a0_square = a[0].square(); + let a1_square = a[1].square(); + let a2_w = w * a[2]; + let a0_a1 = a[0] * a[1]; + + // scalar = (a0^3+wa1^3+w^2a2^3-3wa0a1a2)^-1 + let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2] + - (F::ONE + F::TWO) * a2_w * a0_a1) + .inverse(); + + //scalar*[a0^2-wa1a2, wa2^2-a0a1, a1^2-a0a2] + res[0] = scalar * (a0_square - a[1] * a2_w); + res[1] = scalar * (a2_w * a[2] - a0_a1); + res[2] = scalar * (a1_square - a[0] * a[2]); +} + +/// karatsuba multiplication for cubic extension field +#[inline] +pub(crate) fn cubic_mul + Algebra, R2: Algebra, const D: usize>( + a: &[R; D], + b: &[R2; D], + res: &mut [R; D], + w: F, +) { + assert_eq!(D, 3); + // TODO: Test if we should switch to a naive multiplication approach using dot products. + // This is mainly used for a degree 3 extension of Complex so this approach might be faster. + + let a0_b0 = a[0].clone() * b[0].clone(); + let a1_b1 = a[1].clone() * b[1].clone(); + let a2_b2 = a[2].clone() * b[2].clone(); + + res[0] = a0_b0.clone() + + ((a[1].clone() + a[2].clone()) * (b[1].clone() + b[2].clone()) + - a1_b1.clone() + - a2_b2.clone()) + * w; + res[1] = (a[0].clone() + a[1].clone()) * (b[0].clone() + b[1].clone()) + - a0_b0.clone() + - a1_b1.clone() + + a2_b2.clone() * w; + res[2] = (a[0].clone() + a[2].clone()) * (b[0].clone() + b[2].clone()) - a0_b0 - a2_b2 + a1_b1; +} + +/// Section 11.3.6a in Handbook of Elliptic and Hyperelliptic Curve Cryptography. +#[inline] +pub(crate) fn cubic_square, A: Algebra, const D: usize>( + a: &[A; D], + res: &mut [A; D], +) { + assert_eq!(D, 3); + + let w_a2 = a[2].clone() * F::W; + + res[0] = a[0].square() + (a[1].clone() * w_a2.clone()).double(); + res[1] = w_a2 * a[2].clone() + (a[0].clone() * a[1].clone()).double(); + res[2] = a[1].square() + (a[0].clone() * a[2].clone()).double(); +} + +/// Multiplication in a quartic binomial extension field. +/// +/// Makes use of the in built field dot product code. This is optimized for the case that +/// R is a prime field or its packing. +#[inline] +fn quartic_mul(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F) +where + F: Field, + R: Algebra + Algebra, + R2: Algebra, +{ + assert_eq!(D, 4); + let b_r_rev: [R; 5] = [ + b[3].clone().into(), + b[2].clone().into(), + b[1].clone().into(), + b[0].clone().into(), + w.into(), + ]; + + // Constant term = a0*b0 + w(a1*b3 + a2*b2 + a3*b1) + let w_coeff_0 = + R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap()); + res[0] = R::dot_product(&[a[0].clone(), w_coeff_0], b_r_rev[3..].try_into().unwrap()); + + // Linear term = a0*b1 + a1*b0 + w(a2*b3 + a3*b2) + let w_coeff_1 = + R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap()); + res[1] = R::dot_product( + &[a[0].clone(), a[1].clone(), w_coeff_1], + b_r_rev[2..].try_into().unwrap(), + ); + + // Square term = a0*b2 + a1*b1 + a2*b0 + w(a3*b3) + let b3_w = b[3].clone() * w; + res[2] = R::dot_product::<4>( + a[..4].try_into().unwrap(), + &[ + b_r_rev[1].clone(), + b_r_rev[2].clone(), + b_r_rev[3].clone(), + b3_w.into(), + ], + ); + + // Cubic term = a0*b3 + a1*b2 + a2*b1 + a3*b0 + res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap()); +} + +/// Compute the inverse of a quartic binomial extension field element. +#[inline] +fn quartic_inv(a: &[F; D], res: &mut [F; D], w: F) { + assert_eq!(D, 4); + + // We use the fact that the quartic extension is a tower of quadratic extensions. + // We can see this by writing our element as a = a0 + a1·X + a2·X² + a3·X³ = (a0 + a2·X²) + (a1 + a3·X²)·X. + // Explicitly our tower looks like F < F[x]/(X²-w) < F[x]/(X⁴-w). + // Using this, we can compute the inverse of a in three steps: + + // Compute the norm of our element with respect to F[x]/(X²-w). + // This is given by: + // ((a0 + a2·X²) + (a1 + a3·X²)·X) * ((a0 + a2·X²) - (a1 + a3·X²)·X) + // = (a0 + a2·X²)² - (a1 + a3·X²)² + // = (a0² + w·a2² - 2w·a1·a3) + (2·a0·a2 - a1² - w·a3²)·X² + // = norm_0 + norm_1·X² = norm + let neg_a1 = -a[1]; + let a3_w = a[3] * w; + let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]); + let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]); + + // Now we compute the inverse of norm = norm_0 + norm_1·X². + let mut inv = [F::ZERO; 2]; + quadratic_inv(&[norm_0, norm_1], &mut inv, w); + + // Then the inverse of a is given by: + // a⁻¹ = ((a0 + a2·X²) - (a1 + a3·X²)·X)·norm⁻¹ + // = (a0 + a2·X²)·norm⁻¹ - (a1 + a3·X²)·norm⁻¹·X + // Both of these multiplications can be done in the quadratic extension field. + let mut out_evn = [F::ZERO; 2]; + let mut out_odd = [F::ZERO; 2]; + quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w); + quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w); + + res[0] = out_evn[0]; + res[1] = -out_odd[0]; + res[2] = out_evn[1]; + res[3] = -out_odd[1]; +} + +/// Optimized Square function for quadratic extension field. +/// +/// Makes use of the in built field dot product code. This is optimized for the case that +/// R is a prime field or its packing. +#[inline] +pub(crate) fn quartic_square(a: &[R; D], res: &mut [R; D], w: F) +where + F: Field, + R: Algebra, +{ + assert_eq!(D, 4); + + let two_a0 = a[0].double(); + let two_a1 = a[1].double(); + let two_a2 = a[2].double(); + let a2_w = a[2].clone() * w; + let a3_w = a[3].clone() * w; + + // Constant term = a0*a0 + w*a2*a2 + 2*w*a1*a3 + res[0] = R::dot_product( + &[a[0].clone(), a2_w, two_a1], + &[a[0].clone(), a[2].clone(), a3_w.clone()], + ); + + // Linear term = 2*a0*a1 + 2*w*a2*a3) + res[1] = R::dot_product( + &[two_a0.clone(), two_a2.clone()], + &[a[1].clone(), a3_w.clone()], + ); + + // Square term = a1*a1 + w*a3*a3 + 2*a0*a2 + res[2] = R::dot_product( + &[a[1].clone(), a3_w, two_a0.clone()], + &[a[1].clone(), a[3].clone(), a[2].clone()], + ); + + // Cubic term = 2*a0*a3 + 2*a1*a2) + res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].clone(), a[1].clone()]); +} + +/// Multiplication in a quintic binomial extension field. +/// +/// Makes use of the in built field dot product code. This is optimized for the case that +/// R is a prime field or its packing. +fn quintic_mul(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F) +where + F: Field, + R: Algebra + Algebra, + R2: Algebra, +{ + assert_eq!(D, 5); + let b_r_rev: [R; 6] = [ + b[4].clone().into(), + b[3].clone().into(), + b[2].clone().into(), + b[1].clone().into(), + b[0].clone().into(), + w.into(), + ]; + + // Constant term = a0*b0 + w(a1*b4 + a2*b3 + a3*b2 + a4*b1) + let w_coeff_0 = + R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap()); + res[0] = R::dot_product(&[a[0].clone(), w_coeff_0], b_r_rev[4..].try_into().unwrap()); + + // Linear term = a0*b1 + a1*b0 + w(a2*b4 + a3*b3 + a4*b2) + let w_coeff_1 = + R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap()); + res[1] = R::dot_product( + &[a[0].clone(), a[1].clone(), w_coeff_1], + b_r_rev[3..].try_into().unwrap(), + ); + + // Square term = a0*b2 + a1*b1 + a2*b0 + w(a3*b4 + a4*b3) + let w_coeff_2 = + R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap()); + res[2] = R::dot_product( + &[a[0].clone(), a[1].clone(), a[2].clone(), w_coeff_2], + b_r_rev[2..].try_into().unwrap(), + ); + + // Cubic term = a0*b3 + a1*b2 + a2*b1 + a3*b0 + w*a4*b4 + let b4_w = b[4].clone() * w; + res[3] = R::dot_product::<5>( + a[..5].try_into().unwrap(), + &[ + b_r_rev[1].clone(), + b_r_rev[2].clone(), + b_r_rev[3].clone(), + b_r_rev[4].clone(), + b4_w.into(), + ], + ); + + // Quartic term = a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0 + res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap()); +} + +/// Optimized Square function for quintic extension field elements. +/// +/// Makes use of the in built field dot product code. This is optimized for the case that +/// R is a prime field or its packing. +#[inline] +pub(crate) fn quintic_square(a: &[R; D], res: &mut [R; D], w: F) +where + F: Field, + R: Algebra, +{ + assert_eq!(D, 5); + + let two_a0 = a[0].double(); + let two_a1 = a[1].double(); + let two_a2 = a[2].double(); + let two_a3 = a[3].double(); + let w_a3 = a[3].clone() * w; + let w_a4 = a[4].clone() * w; + + // Constant term = a0*a0 + 2*w(a1*a4 + a2*a3) + res[0] = R::dot_product( + &[a[0].clone(), w_a4.clone(), w_a3.clone()], + &[a[0].clone(), two_a1.clone(), two_a2.clone()], + ); + + // Linear term = w*a3*a3 + 2*(a0*a1 + w * a2*a4) + res[1] = R::dot_product( + &[w_a3, two_a0.clone(), w_a4.clone()], + &[a[3].clone(), a[1].clone(), two_a2], + ); + + // Square term = a1*a1 + 2 * (a0*a2 + w*a3*a4) + res[2] = R::dot_product( + &[a[1].clone(), two_a0.clone(), w_a4.clone()], + &[a[1].clone(), a[2].clone(), two_a3], + ); + + // Cubic term = w*a4*a4 + 2*(a0*a3 + a1*a2) + res[3] = R::dot_product( + &[w_a4, two_a0.clone(), two_a1.clone()], + &[a[4].clone(), a[3].clone(), a[2].clone()], + ); + + // Quartic term = a2*a2 + 2*(a0*a4 + a1*a3) + res[4] = R::dot_product( + &[a[2].clone(), two_a0, two_a1], + &[a[2].clone(), a[4].clone(), a[3].clone()], + ); +} + +/// Compute the inverse of a quintic binomial extension field element. +#[inline] +fn quintic_inv, const D: usize>( + a: &BinomialExtensionField, +) -> BinomialExtensionField { + // Writing 'a' for self, we need to compute: `prod_conj = a^{q^4 + q^3 + q^2 + q}` + let a_exp_q = a.frobenius(); + let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius(); + let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2); + + // norm = a * prod_conj is in the base field, so only compute that + // coefficient rather than the full product. + let a_vals = a.value; + let mut b = prod_conj.value; + b.reverse(); + + let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap()); + let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]); + debug_assert_eq!(BinomialExtensionField::::from(norm), *a * prod_conj); + + prod_conj * norm.inverse() +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/complex.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/complex.rs new file mode 100644 index 00000000..6db81515 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/complex.rs @@ -0,0 +1,124 @@ +use super::{BinomialExtensionField, BinomiallyExtendable, HasTwoAdicBinomialExtension}; +use crate::{Algebra, Field, PrimeCharacteristicRing}; + +pub type Complex = BinomialExtensionField; + +/// A field for which `p = 3 (mod 4)`. Equivalently, `-1` is not a square, +/// so the complex extension can be defined `F[i] = F[X]/(X^2+1)`. +pub trait ComplexExtendable: Field { + /// The two-adicity of `p+1`, the order of the circle group. + const CIRCLE_TWO_ADICITY: usize; + + const COMPLEX_GENERATOR: Complex; + + fn circle_two_adic_generator(bits: usize) -> Complex; +} + +impl BinomiallyExtendable<2> for F { + const W: Self = F::NEG_ONE; + + // since `p = 3 (mod 4)`, `(p-1)/2` is always odd, + // so `(-1)^((p-1)/2) = -1` + const DTH_ROOT: Self = F::NEG_ONE; + + const EXT_GENERATOR: [Self; 2] = F::COMPLEX_GENERATOR.value; +} + +/// Convenience methods for complex extensions +impl Complex { + #[inline(always)] + pub const fn new_complex(real: R, imag: R) -> Self { + Self::new([real, imag]) + } + + #[inline(always)] + pub const fn new_real(real: R) -> Self { + Self::new_complex(real, R::ZERO) + } + + #[inline(always)] + pub const fn new_imag(imag: R) -> Self { + Self::new_complex(R::ZERO, imag) + } + + #[inline(always)] + pub fn real(&self) -> R { + self.value[0].clone() + } + + #[inline(always)] + pub fn imag(&self) -> R { + self.value[1].clone() + } + + #[inline(always)] + pub fn conjugate(&self) -> Self { + Self::new_complex(self.real(), self.imag().neg()) + } + + #[inline] + pub fn norm(&self) -> R { + self.real().square() + self.imag().square() + } + + #[inline(always)] + pub fn to_array(&self) -> [R; 2] { + self.value.clone() + } + + // Sometimes we want to rotate over an extension that's not necessarily ComplexExtendable, + // but still on the circle. + pub fn rotate>(&self, rhs: &Complex) -> Complex { + Complex::::new_complex( + rhs.real() * self.real() - rhs.imag() * self.imag(), + rhs.imag() * self.real() + rhs.real() * self.imag(), + ) + } +} + +/// The complex extension of this field has a binomial extension. +/// +/// This exists if the polynomial ring `F[i][X]` has an irreducible polynomial `X^d-W` +/// allowing us to define the binomial extension field `F[i][X]/(X^d-W)`. +pub trait HasComplexBinomialExtension: ComplexExtendable { + const W: Complex; + + // DTH_ROOT = W^((n - 1)/D). + // n is the order of base field. + // Only works when exists k such that n = kD + 1. + const DTH_ROOT: Complex; + + const EXT_GENERATOR: [Complex; D]; +} + +impl BinomiallyExtendable for Complex +where + F: HasComplexBinomialExtension, +{ + const W: Self = >::W; + + const DTH_ROOT: Self = >::DTH_ROOT; + + const EXT_GENERATOR: [Self; D] = F::EXT_GENERATOR; +} + +/// The complex extension of this field has a two-adic binomial extension. +pub trait HasTwoAdicComplexBinomialExtension: + HasComplexBinomialExtension +{ + const COMPLEX_EXT_TWO_ADICITY: usize; + + fn complex_ext_two_adic_generator(bits: usize) -> [Complex; D]; +} + +impl HasTwoAdicBinomialExtension for Complex +where + F: HasTwoAdicComplexBinomialExtension, +{ + const EXT_TWO_ADICITY: usize = F::COMPLEX_EXT_TWO_ADICITY; + + #[inline(always)] + fn ext_two_adic_generator(bits: usize) -> [Self; D] { + F::complex_ext_two_adic_generator(bits) + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/mod.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/mod.rs new file mode 100644 index 00000000..d0639750 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/mod.rs @@ -0,0 +1,83 @@ +use core::iter; + +use crate::ExtensionField; +use crate::field::Field; + +mod binomial_extension; +mod complex; +mod packed_binomial_extension; + +use alloc::vec::Vec; + +pub use binomial_extension::*; +pub use complex::*; +pub use packed_binomial_extension::*; + +/// Trait for fields that support binomial extension of the form `F[X]/(X^D - W)`. +/// +/// A type implementing this trait can define a degree-`D` extension field using an +/// irreducible binomial polynomial `X^D - W`, where `W` is a nonzero constant in the base field. +/// +/// This is used to construct extension fields with efficient arithmetic. +pub trait BinomiallyExtendable: Field { + /// The constant coefficient `W` in the binomial `X^D - W`. + const W: Self; + + /// A `D`-th root of unity derived from `W`. + /// + /// This is `W^((n - 1)/D)`, where `n` is the order of the field. + /// Valid only when `n = kD + 1` for some `k`. + const DTH_ROOT: Self; + + /// A generator for the extension field, expressed as a degree-`D` polynomial. + /// + /// This is an array of size `D`, where each entry is a base field element. + const EXT_GENERATOR: [Self; D]; +} + +/// Trait for extension fields that support Frobenius automorphisms. +/// +/// The Frobenius automorphism is a field map `x ↦ x^n`, +/// where `n` is the order of the base field. +/// +/// This map is an automorphism of the field that fixes the base field. +pub trait HasFrobenius: ExtensionField { + /// Apply the Frobenius automorphism once. + /// + /// Equivalent to raising the element to the `n`th power. + fn frobenius(&self) -> Self; + + /// Apply the Frobenius automorphism `count` times. + /// + /// Equivalent to raising to the `n^count` power. + fn repeated_frobenius(&self, count: usize) -> Self; + + /// Compute the inverse Frobenius map. + /// + /// Returns the unique element `y` such that `self = y^n`. + fn frobenius_inv(&self) -> Self; + + /// Returns the full Galois orbit of the element under Frobenius. + /// + /// This is the sequence `[x, x^n, x^{n^2}, ..., x^{n^{D-1}}]`, + /// where `D` is the extension degree. + fn galois_orbit(self) -> Vec { + iter::successors(Some(self), |x| Some(x.frobenius())) + .take(Self::DIMENSION) + .collect() + } +} + +/// Trait for binomial extensions that support a two-adic subgroup generator. +pub trait HasTwoAdicBinomialExtension: BinomiallyExtendable { + /// Two-adicity of the multiplicative group of the extension field. + /// + /// This is the number of times 2 divides the order of the field minus 1. + const EXT_TWO_ADICITY: usize; + + /// Returns a two-adic generator for the extension field. + /// + /// This is used to generate the 2^bits-th roots of unity in the extension field. + /// Behavior is undefined if `bits > EXT_TWO_ADICITY`. + fn ext_two_adic_generator(bits: usize) -> [Self; D]; +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/packed_binomial_extension.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/packed_binomial_extension.rs new file mode 100644 index 00000000..afaebcc1 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/extension/packed_binomial_extension.rs @@ -0,0 +1,522 @@ +use alloc::vec::Vec; +use core::array; +use core::fmt::Debug; +use core::iter::{Product, Sum}; +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +use itertools::Itertools; +use p3_util::{flatten_to_base, reconstitute_from_base}; +use rand::distr::{Distribution, StandardUniform}; +use serde::{Deserialize, Serialize}; + +use super::{ + BinomialExtensionField, binomial_mul, cubic_square, quartic_square, quintic_square, vector_add, + vector_sub, +}; +use crate::extension::BinomiallyExtendable; +use crate::{ + Algebra, BasedVectorSpace, Field, PackedField, PackedFieldExtension, PackedValue, Powers, + PrimeCharacteristicRing, field_to_array, +}; + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord)] +#[repr(transparent)] // Needed to make various casts safe. +pub struct PackedBinomialExtensionField, const D: usize> { + #[serde( + with = "p3_util::array_serialization", + bound(serialize = "PF: Serialize", deserialize = "PF: Deserialize<'de>") + )] + pub(crate) value: [PF; D], +} + +impl, const D: usize> PackedBinomialExtensionField { + const fn new(value: [PF; D]) -> Self { + Self { value } + } +} + +impl, const D: usize> Default + for PackedBinomialExtensionField +{ + #[inline] + fn default() -> Self { + Self { + value: array::from_fn(|_| PF::ZERO), + } + } +} + +impl, const D: usize> From> + for PackedBinomialExtensionField +{ + #[inline] + fn from(x: BinomialExtensionField) -> Self { + Self { + value: x.value.map(Into::into), + } + } +} + +impl, const D: usize> From + for PackedBinomialExtensionField +{ + #[inline] + fn from(x: PF) -> Self { + Self { + value: field_to_array(x), + } + } +} + +impl, const D: usize> + Distribution> for StandardUniform +where + Self: Distribution, +{ + #[inline] + fn sample(&self, rng: &mut R) -> PackedBinomialExtensionField { + PackedBinomialExtensionField::new(array::from_fn(|_| self.sample(rng))) + } +} + +impl, PF: PackedField, const D: usize> + Algebra> for PackedBinomialExtensionField +{ +} + +impl, PF: PackedField, const D: usize> Algebra + for PackedBinomialExtensionField +{ +} + +impl PrimeCharacteristicRing for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type PrimeSubfield = PF::PrimeSubfield; + + const ZERO: Self = Self { + value: [PF::ZERO; D], + }; + + const ONE: Self = Self { + value: field_to_array(PF::ONE), + }; + + const TWO: Self = Self { + value: field_to_array(PF::TWO), + }; + + const NEG_ONE: Self = Self { + value: field_to_array(PF::NEG_ONE), + }; + + #[inline] + fn from_prime_subfield(val: Self::PrimeSubfield) -> Self { + PF::from_prime_subfield(val).into() + } + + #[inline] + fn from_bool(b: bool) -> Self { + PF::from_bool(b).into() + } + + #[inline(always)] + fn square(&self) -> Self { + let mut res = Self::default(); + let w = F::W; + match D { + 2 => { + let a = &self.value; + let a1_w = a[1] * F::W; + res.value[0] = PF::dot_product(a[..].try_into().unwrap(), &[a[0], a1_w]); + res.value[1] = a[0] * a[1].double(); + } + 3 => cubic_square(&self.value, &mut res.value), + 4 => quartic_square(&self.value, &mut res.value, w), + 5 => quintic_square(&self.value, &mut res.value, w), + _ => binomial_mul::(&self.value, &self.value, &mut res.value, w), + } + res + } + + #[inline] + fn zero_vec(len: usize) -> Vec { + // SAFETY: this is a repr(transparent) wrapper around an array. + unsafe { reconstitute_from_base(PF::zero_vec(len * D)) } + } +} + +impl BasedVectorSpace for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + const DIMENSION: usize = D; + + #[inline] + fn as_basis_coefficients_slice(&self) -> &[PF] { + &self.value + } + + #[inline] + fn from_basis_coefficients_fn PF>(f: Fn) -> Self { + Self { + value: array::from_fn(f), + } + } + + #[inline] + fn from_basis_coefficients_iter>(mut iter: I) -> Option { + (iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) // The unwrap is safe as we just checked the length of iter. + } + + #[inline] + fn flatten_to_base(vec: Vec) -> Vec { + unsafe { + // Safety: + // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[PF; D]` + flatten_to_base(vec) + } + } + + #[inline] + fn reconstitute_from_base(vec: Vec) -> Vec { + unsafe { + // Safety: + // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[PF; D]` + reconstitute_from_base(vec) + } + } +} + +impl PackedFieldExtension> + for PackedBinomialExtensionField +where + F: BinomiallyExtendable, +{ + #[inline] + fn from_ext_slice(ext_slice: &[BinomialExtensionField]) -> Self { + let width = F::Packing::WIDTH; + assert_eq!(ext_slice.len(), width); + + let res = array::from_fn(|i| F::Packing::from_fn(|j| ext_slice[j].value[i])); + Self::new(res) + } + + #[inline] + fn to_ext_iter( + iter: impl IntoIterator, + ) -> impl Iterator> { + let width = F::Packing::WIDTH; + iter.into_iter().flat_map(move |x| { + (0..width).map(move |i| { + let values = array::from_fn(|j| x.value[j].as_slice()[i]); + BinomialExtensionField::new(values) + }) + }) + } + + #[inline] + fn packed_ext_powers(base: BinomialExtensionField) -> crate::Powers { + let width = F::Packing::WIDTH; + let powers = base.powers().take(width + 1).collect_vec(); + // Transpose first WIDTH powers + let current = Self::from_ext_slice(&powers[..width]); + + // Broadcast self^WIDTH + let multiplier = powers[width].into(); + + Powers { + base: multiplier, + current, + } + } +} + +impl Neg for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn neg(self) -> Self { + Self { + value: self.value.map(PF::neg), + } + } +} + +impl Add for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + let value = vector_add(&self.value, &rhs.value); + Self { value } + } +} + +impl Add> + for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn add(self, rhs: BinomialExtensionField) -> Self { + let value = vector_add(&self.value, &rhs.value); + Self { value } + } +} + +impl Add for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn add(mut self, rhs: PF) -> Self { + self.value[0] += rhs; + self + } +} + +impl AddAssign for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn add_assign(&mut self, rhs: Self) { + for i in 0..D { + self.value[i] += rhs.value[i]; + } + } +} + +impl AddAssign> + for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn add_assign(&mut self, rhs: BinomialExtensionField) { + for i in 0..D { + self.value[i] += rhs.value[i]; + } + } +} + +impl AddAssign for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn add_assign(&mut self, rhs: PF) { + self.value[0] += rhs; + } +} + +impl Sum for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn sum>(iter: I) -> Self { + iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO) + } +} + +impl Sub for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + let value = vector_sub(&self.value, &rhs.value); + Self { value } + } +} + +impl Sub> + for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: BinomialExtensionField) -> Self { + let value = vector_sub(&self.value, &rhs.value); + Self { value } + } +} + +impl Sub for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn sub(self, rhs: PF) -> Self { + let mut res = self.value; + res[0] -= rhs; + Self { value: res } + } +} + +impl SubAssign for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl SubAssign> + for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn sub_assign(&mut self, rhs: BinomialExtensionField) { + *self = *self - rhs; + } +} + +impl SubAssign for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn sub_assign(&mut self, rhs: PF) { + *self = *self - rhs; + } +} + +impl Mul for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + let a = self.value; + let b = rhs.value; + let mut res = Self::default(); + let w = F::W; + + binomial_mul::(&a, &b, &mut res.value, w); + + res + } +} + +impl Mul> + for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn mul(self, rhs: BinomialExtensionField) -> Self { + let a = self.value; + let b = rhs.value; + let mut res = Self::default(); + let w = F::W; + + binomial_mul(&a, &b, &mut res.value, w); + + res + } +} + +impl Mul for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + type Output = Self; + + #[inline] + fn mul(self, rhs: PF) -> Self { + Self { + value: self.value.map(|x| x * rhs), + } + } +} + +impl Product for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn product>(iter: I) -> Self { + iter.reduce(|acc, x| acc * x).unwrap_or(Self::ZERO) + } +} + +impl MulAssign for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl MulAssign> + for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn mul_assign(&mut self, rhs: BinomialExtensionField) { + *self = *self * rhs; + } +} + +impl MulAssign for PackedBinomialExtensionField +where + F: BinomiallyExtendable, + PF: PackedField, +{ + #[inline] + fn mul_assign(&mut self, rhs: PF) { + *self = *self * rhs; + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/field.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/field.rs new file mode 100644 index 00000000..b047cd1d --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/field.rs @@ -0,0 +1,980 @@ +use alloc::vec; +use alloc::vec::Vec; +use core::fmt::{Debug, Display}; +use core::hash::Hash; +use core::iter::{Product, Sum}; +use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; +use core::{array, slice}; + +use num_bigint::BigUint; +use p3_maybe_rayon::prelude::{ParallelIterator, ParallelSlice}; +use p3_util::iter_array_chunks_padded; +use serde::Serialize; +use serde::de::DeserializeOwned; + +use crate::exponentiation::bits_u64; +use crate::integers::{QuotientMap, from_integer_types}; +use crate::packed::PackedField; +use crate::{Packable, PackedFieldExtension, PackedValue}; + +/// A commutative ring, `R`, with prime characteristic, `p`. +/// +/// This permits elements like: +/// - A single finite field element. +/// - A symbolic expression which would evaluate to a field element. +/// - An array of finite field elements. +/// - A polynomial with coefficients in a finite field. +/// +/// ### Mathematical Description +/// +/// Mathematically, a commutative ring is a set of objects which supports an addition-like +/// like operation, `+`, and a multiplication-like operation `*`. +/// +/// Let `x, y, z` denote arbitrary elements of the set. +/// +/// Then, an operation is addition-like if it satisfies the following properties: +/// - Commutativity => `x + y = y + x` +/// - Associativity => `x + (y + z) = (x + y) + z` +/// - Unit => There exists an identity element `ZERO` satisfying `x + ZERO = x`. +/// - Inverses => For every `x` there exists a unique inverse `(-x)` satisfying `x + (-x) = ZERO` +/// +/// Similarly, an operation is multiplication-like if it satisfies the following properties: +/// - Commutativity => `x * y = y * x` +/// - Associativity => `x * (y * z) = (x * y) * z` +/// - Unit => There exists an identity element `ONE` satisfying `x * ONE = x`. +/// - Distributivity => The two operations `+` and `*` must together satisfy `x * (y + z) = (x * y) + (x * z)` +/// +/// Unlike in the addition case, we do not require inverses to exist with respect to `*`. +/// +/// The simplest examples of commutative rings are the integers (`ℤ`), and the integers mod `N` (`ℤ/N`). +/// +/// The characteristic of a ring is the smallest positive integer `r` such that `0 = r . 1 = 1 + 1 + ... + 1 (r times)`. +/// For example, the characteristic of the modulo ring `ℤ/N` is `N`. +/// +/// Rings with prime characteristic are particularly special due to their close relationship with finite fields. +pub trait PrimeCharacteristicRing: + Sized + + Default + + Clone + + Add + + AddAssign + + Sub + + SubAssign + + Neg + + Mul + + MulAssign + + Sum + + Product + + Debug +{ + /// The field `ℤ/p` where the characteristic of this ring is p. + type PrimeSubfield: PrimeField; + + /// The additive identity of the ring. + /// + /// For every element `a` in the ring we require the following properties: + /// + /// `a + ZERO = ZERO + a = a,` + /// + /// `a + (-a) = (-a) + a = ZERO.` + const ZERO: Self; + + /// The multiplicative identity of the ring. + /// + /// For every element `a` in the ring we require the following property: + /// + /// `a*ONE = ONE*a = a.` + const ONE: Self; + + /// The element in the ring given by `ONE + ONE`. + /// + /// This is provided as a convenience as `TWO` occurs regularly in + /// the proving system. This also is slightly faster than computing + /// it via addition. Note that multiplication by `TWO` is discouraged. + /// Instead of `a * TWO` use `a.double()` which will be faster. + /// + /// If the field has characteristic 2 this is equal to ZERO. + const TWO: Self; + + /// The element in the ring given by `-ONE`. + /// + /// This is provided as a convenience as `NEG_ONE` occurs regularly in + /// the proving system. This also is slightly faster than computing + /// it via negation. Note that where possible `NEG_ONE` should be absorbed + /// into mathematical operations. For example `a - b` will be faster + /// than `a + NEG_ONE * b` and similarly `(-b)` is faster than `NEG_ONE * b`. + /// + /// If the field has characteristic 2 this is equal to ONE. + const NEG_ONE: Self; + + /// Embed an element of the prime field `ℤ/p` into the ring `R`. + /// + /// Given any element `[r] ∈ ℤ/p`, represented by an integer `r` between `0` and `p - 1` + /// `from_prime_subfield([r])` will be equal to: + /// + /// `Self::ONE + ... + Self::ONE (r times)` + fn from_prime_subfield(f: Self::PrimeSubfield) -> Self; + + /// Return `Self::ONE` if `b` is `true` and `Self::ZERO` if `b` is `false`. + #[must_use] + #[inline(always)] + fn from_bool(b: bool) -> Self { + // Some rings might reimplement this to avoid the branch. + if b { Self::ONE } else { Self::ZERO } + } + + from_integer_types!( + u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize + ); + + /// The elementary function `double(a) = 2*a`. + /// + /// This function should be preferred over calling `a + a` or `TWO * a` as a faster implementation may be available for some rings. + /// If the field has characteristic 2 then this returns 0. + #[must_use] + #[inline(always)] + fn double(&self) -> Self { + self.clone() + self.clone() + } + + /// The elementary function `square(a) = a^2`. + /// + /// This function should be preferred over calling `a * a`, as a faster implementation may be available for some rings. + #[must_use] + #[inline(always)] + fn square(&self) -> Self { + self.clone() * self.clone() + } + + /// The elementary function `cube(a) = a^3`. + /// + /// This function should be preferred over calling `a * a * a`, as a faster implementation may be available for some rings. + #[must_use] + #[inline(always)] + fn cube(&self) -> Self { + self.square() * self.clone() + } + + /// Computes the arithmetic generalization of boolean `xor`. + /// + /// For boolean inputs, `x ^ y = x + y - 2 xy`. + #[must_use] + #[inline(always)] + fn xor(&self, y: &Self) -> Self { + self.clone() + y.clone() - self.clone() * y.clone().double() + } + + /// Computes the arithmetic generalization of a triple `xor`. + /// + /// For boolean inputs `x ^ y ^ z = x + y + z - 2(xy + xz + yz) + 4xyz`. + #[must_use] + #[inline(always)] + fn xor3(&self, y: &Self, z: &Self) -> Self { + self.xor(y).xor(z) + } + + /// Computes the arithmetic generalization of `andnot`. + /// + /// For boolean inputs `(!x) & y = (1 - x)y`. + #[must_use] + #[inline(always)] + fn andn(&self, y: &Self) -> Self { + (Self::ONE - self.clone()) * y.clone() + } + + /// The vanishing polynomial for boolean values: `x * (1 - x)`. + /// + /// This is a polynomial of degree `2` that evaluates to `0` if the input is `0` or `1`. + /// If our space is a field, then this will be nonzero on all other inputs. + #[must_use] + #[inline(always)] + fn bool_check(&self) -> Self { + // We use `x * (1 - x)` instead of `x * (x - 1)` as this lets us delegate to the `andn` function. + self.andn(self) + } + + /// Exponentiation by a `u64` power. + /// + /// This uses the standard square and multiply approach. + /// For specific powers regularly used and known in advance, + /// this will be slower than custom addition chain exponentiation. + #[must_use] + #[inline] + fn exp_u64(&self, power: u64) -> Self { + let mut current = self.clone(); + let mut product = Self::ONE; + + for j in 0..bits_u64(power) { + if (power >> j) & 1 != 0 { + product *= current.clone(); + } + current = current.square(); + } + product + } + + /// Exponentiation by a small constant power. + /// + /// For a collection of small values we implement custom multiplication chain circuits which can be faster than the + /// simpler square and multiply approach. + /// + /// For large values this defaults back to `self.exp_u64`. + #[must_use] + #[inline(always)] + fn exp_const_u64(&self) -> Self { + match POWER { + 0 => Self::ONE, + 1 => self.clone(), + 2 => self.square(), + 3 => self.cube(), + 4 => self.square().square(), + 5 => self.square().square() * self.clone(), + 6 => self.square().cube(), + 7 => { + let x2 = self.square(); + let x3 = x2.clone() * self.clone(); + let x4 = x2.square(); + x3 * x4 + } + _ => self.exp_u64(POWER), + } + } + + /// The elementary function `exp_power_of_2(a, power_log) = a^{2^power_log}`. + /// + /// Computed via repeated squaring. + #[must_use] + #[inline] + fn exp_power_of_2(&self, power_log: usize) -> Self { + let mut res = self.clone(); + for _ in 0..power_log { + res = res.square(); + } + res + } + + /// The elementary function `mul_2exp_u64(a, exp) = a * 2^{exp}`. + /// + /// Here `2^{exp}` is computed using the square and multiply approach. + #[must_use] + #[inline] + fn mul_2exp_u64(&self, exp: u64) -> Self { + self.clone() * Self::TWO.exp_u64(exp) + } + + /// Construct an iterator which returns powers of `self`: `self^0, self^1, self^2, ...`. + #[must_use] + #[inline] + fn powers(&self) -> Powers { + self.shifted_powers(Self::ONE) + } + + /// Construct an iterator which returns powers of `self` shifted by `start`: `start, start*self^1, start*self^2, ...`. + #[must_use] + #[inline] + fn shifted_powers(&self, start: Self) -> Powers { + Powers { + base: self.clone(), + current: start, + } + } + + /// Compute the dot product of two vectors. + #[must_use] + #[inline] + fn dot_product(u: &[Self; N], v: &[Self; N]) -> Self { + u.iter().zip(v).map(|(x, y)| x.clone() * y.clone()).sum() + } + + /// Compute the sum of a slice of elements whose length is a compile time constant. + /// + /// The rust compiler doesn't realize that add is associative + /// so we help it out and minimize the dependency chains by hand. + /// Thus while this function has the same throughput as `input.iter().sum()`, + /// it will usually have much lower latency. + /// + /// # Panics + /// + /// May panic if the length of the input slice is not equal to `N`. + #[must_use] + #[inline] + fn sum_array(input: &[Self]) -> Self { + // It looks a little strange but using a const parameter and an assert_eq! instead of + // using input.len() leads to a significant performance improvement. + // We could make this input &[Self; N] but that would require sticking .try_into().unwrap() everywhere. + // Checking godbolt, the compiler seems to unroll everything anyway. + assert_eq!(N, input.len()); + + // For `N <= 8` we implement a tree sum structure and for `N > 8` we break the input into + // chunks of `8`, perform a tree sum on each chunk and sum the results. The parameter `8` + // was determined experimentally by testing the speed of the poseidon2 internal layer computations. + // This is a useful benchmark as we have a mix of summations of size 15, 23 with other work in between. + // I only tested this on `AVX2` though so there might be a better value for other architectures. + match N { + 0 => Self::ZERO, + 1 => input[0].clone(), + 2 => input[0].clone() + input[1].clone(), + 3 => input[0].clone() + input[1].clone() + input[2].clone(), + 4 => (input[0].clone() + input[1].clone()) + (input[2].clone() + input[3].clone()), + 5 => Self::sum_array::<4>(&input[..4]) + Self::sum_array::<1>(&input[4..]), + 6 => Self::sum_array::<4>(&input[..4]) + Self::sum_array::<2>(&input[4..]), + 7 => Self::sum_array::<4>(&input[..4]) + Self::sum_array::<3>(&input[4..]), + 8 => Self::sum_array::<4>(&input[..4]) + Self::sum_array::<4>(&input[4..]), + _ => { + // We know that N > 8 here so this saves an add over the usual + // initialisation of acc to Self::ZERO. + let mut acc = Self::sum_array::<8>(&input[..8]); + for i in (16..=N).step_by(8) { + acc += Self::sum_array::<8>(&input[(i - 8)..i]) + } + // This would be much cleaner if we could use const generic expressions but + // this will do for now. + match N & 7 { + 0 => acc, + 1 => acc + Self::sum_array::<1>(&input[(8 * (N / 8))..]), + 2 => acc + Self::sum_array::<2>(&input[(8 * (N / 8))..]), + 3 => acc + Self::sum_array::<3>(&input[(8 * (N / 8))..]), + 4 => acc + Self::sum_array::<4>(&input[(8 * (N / 8))..]), + 5 => acc + Self::sum_array::<5>(&input[(8 * (N / 8))..]), + 6 => acc + Self::sum_array::<6>(&input[(8 * (N / 8))..]), + 7 => acc + Self::sum_array::<7>(&input[(8 * (N / 8))..]), + _ => unreachable!(), + } + } + } + } + + /// Allocates a vector of zero elements of length `len`. Many operating systems zero pages + /// before assigning them to a userspace process. In that case, our process should not need to + /// write zeros, which would be redundant. However, the compiler may not always recognize this. + /// + /// In particular, `vec![Self::ZERO; len]` appears to result in redundant userspace zeroing. + /// This is the default implementation, but implementors may wish to provide their own + /// implementation which transmutes something like `vec![0u32; len]`. + #[must_use] + #[inline] + fn zero_vec(len: usize) -> Vec { + vec![Self::ZERO; len] + } +} + +/// A vector space `V` over `F` with a fixed basis. Fixing the basis allows elements of `V` to be +/// converted to and from `DIMENSION` many elements of `F` which are interpreted as basis coefficients. +/// +/// We usually expect `F` to be a field but do not enforce this and so allow it to be just a ring. +/// This lets every ring implement `BasedVectorSpace` and is useful in a couple of other cases. +/// +/// ## Safety +/// We make no guarantees about consistency of the choice of basis across different versions of Plonky3. +/// If this choice of basis changes, the behaviour of `BasedVectorSpace` will also change. Due to this, +/// we recommend avoiding using this trait unless absolutely necessary. +/// +/// ### Mathematical Description +/// Given a vector space, `A` over `F`, a basis is a set of elements `B = {b_0, ..., b_{n-1}}` +/// in `A` such that, given any element `a`, we can find a unique set of `n` elements of `F`, +/// `f_0, ..., f_{n - 1}` satisfying `a = f_0 b_0 + ... + f_{n - 1} b_{n - 1}`. Thus the choice +/// of `B` gives rise to a natural linear map between the vector space `A` and the canonical +/// `n` dimensional vector space `F^n`. +/// +/// This allows us to map between elements of `A` and arrays of `n` elements of `F`. +/// Clearly this map depends entirely on the choice of basis `B` which may change +/// across versions of Plonky3. +/// +/// The situation is slightly more complicated in cases where `F` is not a field but boils down +/// to an identical description once we enforce that `A` is a free module over `F`. +pub trait BasedVectorSpace: Sized { + /// The dimension of the vector space, i.e. the number of elements in + /// its basis. + const DIMENSION: usize; + + /// Fixes a basis for the algebra `A` and uses this to + /// map an element of `A` to a slice of `DIMENSION` `F` elements. + /// + /// # Safety + /// + /// The value produced by this function fundamentally depends + /// on the choice of basis. Care must be taken + /// to ensure portability if these values might ever be passed to + /// (or rederived within) another compilation environment where a + /// different basis might have been used. + #[must_use] + fn as_basis_coefficients_slice(&self) -> &[F]; + + /// Fixes a basis for the algebra `A` and uses this to + /// map `DIMENSION` `F` elements to an element of `A`. + /// + /// # Safety + /// + /// The value produced by this function fundamentally depends + /// on the choice of basis. Care must be taken + /// to ensure portability if these values might ever be passed to + /// (or rederived within) another compilation environment where a + /// different basis might have been used. + /// + /// Returns `None` if the length of the slice is different to `DIMENSION`. + #[must_use] + #[inline] + fn from_basis_coefficients_slice(slice: &[F]) -> Option { + Self::from_basis_coefficients_iter(slice.iter().cloned()) + } + + /// Fixes a basis for the algebra `A` and uses this to + /// map `DIMENSION` `F` elements to an element of `A`. Similar + /// to `core:array::from_fn`, the `DIMENSION` `F` elements are + /// given by `Fn(0), ..., Fn(DIMENSION - 1)` called in that order. + /// + /// # Safety + /// + /// The value produced by this function fundamentally depends + /// on the choice of basis. Care must be taken + /// to ensure portability if these values might ever be passed to + /// (or rederived within) another compilation environment where a + /// different basis might have been used. + #[must_use] + fn from_basis_coefficients_fn F>(f: Fn) -> Self; + + /// Fixes a basis for the algebra `A` and uses this to + /// map `DIMENSION` `F` elements to an element of `A`. + /// + /// # Safety + /// + /// The value produced by this function fundamentally depends + /// on the choice of basis. Care must be taken + /// to ensure portability if these values might ever be passed to + /// (or rederived within) another compilation environment where a + /// different basis might have been used. + /// + /// Returns `None` if the length of the iterator is different to `DIMENSION`. + #[must_use] + fn from_basis_coefficients_iter>(iter: I) -> Option; + + /// Given a basis for the Algebra `A`, return the i'th basis element. + /// + /// # Safety + /// + /// The value produced by this function fundamentally depends + /// on the choice of basis. Care must be taken + /// to ensure portability if these values might ever be passed to + /// (or rederived within) another compilation environment where a + /// different basis might have been used. + /// + /// Returns `None` if `i` is greater than or equal to `DIMENSION`. + #[must_use] + #[inline] + fn ith_basis_element(i: usize) -> Option { + (i < Self::DIMENSION).then(|| Self::from_basis_coefficients_fn(|j| F::from_bool(i == j))) + } + + /// Convert from a vector of `Self` to a vector of `F` by flattening the basis coefficients. + /// + /// Depending on the `BasedVectorSpace` this may be essentially a no-op and should certainly + /// be reimplemented in those cases. + /// + /// # Safety + /// + /// The value produced by this function fundamentally depends + /// on the choice of basis. Care must be taken + /// to ensure portability if these values might ever be passed to + /// (or rederived within) another compilation environment where a + /// different basis might have been used. + #[must_use] + #[inline] + fn flatten_to_base(vec: Vec) -> Vec { + vec.into_iter() + .flat_map(|x| x.as_basis_coefficients_slice().to_vec()) + .collect() + } + + /// Convert from a vector of `F` to a vector of `Self` by combining the basis coefficients. + /// + /// Depending on the `BasedVectorSpace` this may be essentially a no-op and should certainly + /// be reimplemented in those cases. + /// + /// # Panics + /// This will panic if the length of `vec` is not a multiple of `Self::DIMENSION`. + /// + /// # Safety + /// + /// The value produced by this function fundamentally depends + /// on the choice of basis. Care must be taken + /// to ensure portability if these values might ever be passed to + /// (or rederived within) another compilation environment where a + /// different basis might have been used. + #[must_use] + #[inline] + fn reconstitute_from_base(vec: Vec) -> Vec + where + F: Sync, + Self: Send, + { + assert_eq!(vec.len() % Self::DIMENSION, 0); + + vec.par_chunks_exact(Self::DIMENSION) + .map(|chunk| { + Self::from_basis_coefficients_slice(chunk) + .expect("Chunk length not equal to dimension") + }) + .collect() + } +} + +impl BasedVectorSpace for F { + const DIMENSION: usize = 1; + + #[inline] + fn as_basis_coefficients_slice(&self) -> &[F] { + slice::from_ref(self) + } + + #[inline] + fn from_basis_coefficients_fn F>(mut f: Fn) -> Self { + f(0) + } + + #[inline] + fn from_basis_coefficients_iter>(mut iter: I) -> Option { + (iter.len() == 1).then(|| iter.next().unwrap()) // Unwrap will not panic as we know the length is 1. + } + + #[inline] + fn flatten_to_base(vec: Vec) -> Vec { + vec + } + + #[inline] + fn reconstitute_from_base(vec: Vec) -> Vec { + vec + } +} + +/// A ring implements `InjectiveMonomial` if the algebraic function +/// `f(x) = x^N` is an injective map on elements of the ring. +/// +/// We do not enforce that this map be invertible as there are useful +/// cases such as polynomials or symbolic expressions where no inverse exists. +/// +/// However, if the ring is a field with order `q` or an array of such field elements, +/// then `f(x) = x^N` will be injective if and only if it is invertible and so in +/// such cases this monomial acts as a permutation. Moreover, this will occur +/// exactly when `N` and `q - 1` are relatively prime i.e. `gcd(N, q - 1) = 1`. +pub trait InjectiveMonomial: PrimeCharacteristicRing { + /// Compute `x -> x^n` for a given `n > 1` such that this + /// map is injective. + #[must_use] + #[inline] + fn injective_exp_n(&self) -> Self { + self.exp_const_u64::() + } +} + +/// A ring implements `PermutationMonomial` if the algebraic function +/// `f(x) = x^N` is invertible and thus acts as a permutation on elements of the ring. +/// +/// In all cases we care about, this means that we can find another integer `K` such +/// that `x = x^{NK}` for all elements of our ring. +pub trait PermutationMonomial: InjectiveMonomial { + /// Compute `x -> x^K` for a given `K > 1` such that + /// `x^{NK} = x` for all elements `x`. + #[must_use] + fn injective_exp_root_n(&self) -> Self; +} + +/// A ring `R` implements `Algebra` if there is an injective homomorphism +/// from `F` into `R`; in particular only `F::ZERO` maps to `R::ZERO`. +/// +/// For the most part, we will usually expect `F` to be a field but there +/// are a few cases where it is handy to allow it to just be a ring. In +/// particular, every ring naturally implements `Algebra`. +/// +/// ### Mathematical Description +/// +/// Let `x` and `y` denote arbitrary elements of `F`. Then +/// we require that our map `from` has the properties: +/// - Preserves Identity: `from(F::ONE) = R::ONE` +/// - Commutes with Addition: `from(x + y) = from(x) + from(y)` +/// - Commutes with Multiplication: `from(x * y) = from(x) * from(y)` +/// +/// Such maps are known as ring homomorphisms and are injective if the +/// only element which maps to `R::ZERO` is `F::ZERO`. +/// +/// The existence of this map makes `R` into an `F`-module and hence an `F`-algebra. +/// If, additionally, `R` is a field, then this makes `R` a field extension of `F`. +pub trait Algebra: + PrimeCharacteristicRing + + From + + Add + + AddAssign + + Sub + + SubAssign + + Mul + + MulAssign +{ +} + +// Every ring is an algebra over itself. +impl Algebra for R {} + +/// A collection of methods designed to help hash field elements. +/// +/// Most fields will want to reimplement many/all of these methods as the default implementations +/// are slow and involve converting to/from byte representations. +pub trait RawDataSerializable: Sized { + /// The number of bytes which this field element occupies in memory. + /// Must be equal to the length of self.into_bytes(). + const NUM_BYTES: usize; + + /// Convert a field element into a collection of bytes. + #[must_use] + fn into_bytes(self) -> impl IntoIterator; + + /// Convert an iterator of field elements into an iterator of bytes. + #[must_use] + fn into_byte_stream(input: impl IntoIterator) -> impl IntoIterator { + input.into_iter().flat_map(|elem| elem.into_bytes()) + } + + /// Convert an iterator of field elements into an iterator of u32s. + /// + /// If `NUM_BYTES` does not divide `4`, multiple `F`s may be packed together to make a single `u32`. Furthermore, + /// if `NUM_BYTES * input.len()` does not divide `4`, the final `u32` will involve padding bytes which are set to `0`. + #[must_use] + fn into_u32_stream(input: impl IntoIterator) -> impl IntoIterator { + let bytes = Self::into_byte_stream(input); + iter_array_chunks_padded(bytes, 0).map(u32::from_le_bytes) + } + + /// Convert an iterator of field elements into an iterator of u64s. + /// + /// If `NUM_BYTES` does not divide `8`, multiple `F`s may be packed together to make a single `u64`. Furthermore, + /// if `NUM_BYTES * input.len()` does not divide `8`, the final `u64` will involve padding bytes which are set to `0`. + #[must_use] + fn into_u64_stream(input: impl IntoIterator) -> impl IntoIterator { + let bytes = Self::into_byte_stream(input); + iter_array_chunks_padded(bytes, 0).map(u64::from_le_bytes) + } + + /// Convert an iterator of field element arrays into an iterator of byte arrays. + /// + /// Converts an element `[F; N]` into the byte array `[[u8; N]; NUM_BYTES]`. This is + /// intended for use with vectorized hash functions which use vector operations + /// to compute several hashes in parallel. + #[must_use] + fn into_parallel_byte_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + input.into_iter().flat_map(|vector| { + let bytes = vector.map(|elem| elem.into_bytes().into_iter().collect::>()); + (0..Self::NUM_BYTES).map(move |i| array::from_fn(|j| bytes[j][i])) + }) + } + + /// Convert an iterator of field element arrays into an iterator of u32 arrays. + /// + /// Converts an element `[F; N]` into the u32 array `[[u32; N]; NUM_BYTES/4]`. This is + /// intended for use with vectorized hash functions which use vector operations + /// to compute several hashes in parallel. + /// + /// This function is guaranteed to be equivalent to starting with `Iterator<[F; N]>` performing a transpose + /// operation to get `[Iterator; N]`, calling `into_u32_stream` on each element to get `[Iterator; N]` and then + /// performing another transpose operation to get `Iterator<[u32; N]>`. + /// + /// If `NUM_BYTES` does not divide `4`, multiple `[F; N]`s may be packed together to make a single `[u32; N]`. Furthermore, + /// if `NUM_BYTES * input.len()` does not divide `4`, the final `[u32; N]` will involve padding bytes which are set to `0`. + #[must_use] + fn into_parallel_u32_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + let bytes = Self::into_parallel_byte_streams(input); + iter_array_chunks_padded(bytes, [0; N]).map(|byte_array: [[u8; N]; 4]| { + array::from_fn(|i| u32::from_le_bytes(array::from_fn(|j| byte_array[j][i]))) + }) + } + + /// Convert an iterator of field element arrays into an iterator of u64 arrays. + /// + /// Converts an element `[F; N]` into the u64 array `[[u64; N]; NUM_BYTES/8]`. This is + /// intended for use with vectorized hash functions which use vector operations + /// to compute several hashes in parallel. + /// + /// This function is guaranteed to be equivalent to starting with `Iterator<[F; N]>` performing a transpose + /// operation to get `[Iterator; N]`, calling `into_u64_stream` on each element to get `[Iterator; N]` and then + /// performing another transpose operation to get `Iterator<[u64; N]>`. + /// + /// If `NUM_BYTES` does not divide `8`, multiple `[F; N]`s may be packed together to make a single `[u64; N]`. Furthermore, + /// if `NUM_BYTES * input.len()` does not divide `8`, the final `[u64; N]` will involve padding bytes which are set to `0`. + #[must_use] + fn into_parallel_u64_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + let bytes = Self::into_parallel_byte_streams(input); + iter_array_chunks_padded(bytes, [0; N]).map(|byte_array: [[u8; N]; 8]| { + array::from_fn(|i| u64::from_le_bytes(array::from_fn(|j| byte_array[j][i]))) + }) + } +} + +/// A field `F`. This permits both modular fields `ℤ/p` along with their field extensions. +/// +/// A ring is a field if every element `x` has a unique multiplicative inverse `x^{-1}` +/// which satisfies `x * x^{-1} = F::ONE`. +pub trait Field: + Algebra + + RawDataSerializable + + Packable + + 'static + + Copy + + Div + + Eq + + Hash + + Send + + Sync + + Display + + Serialize + + DeserializeOwned +{ + type Packing: PackedField; + + /// A generator of this field's multiplicative group. + const GENERATOR: Self; + + /// Check if the given field element is equal to the unique additive identity (ZERO). + #[must_use] + #[inline] + fn is_zero(&self) -> bool { + *self == Self::ZERO + } + + /// Check if the given field element is equal to the unique multiplicative identity (ONE). + #[must_use] + #[inline] + fn is_one(&self) -> bool { + *self == Self::ONE + } + + /// The multiplicative inverse of this field element, if it exists. + /// + /// NOTE: The inverse of `0` is undefined and will return `None`. + #[must_use] + fn try_inverse(&self) -> Option; + + /// The multiplicative inverse of this field element. + /// + /// # Panics + /// The function will panic if the field element is `0`. + /// Use try_inverse if you want to handle this case. + #[must_use] + fn inverse(&self) -> Self { + self.try_inverse().expect("Tried to invert zero") + } + + /// The elementary function `halve(a) = a/2`. + /// + /// # Panics + /// The function will panic if the field has characteristic 2. + #[must_use] + fn halve(&self) -> Self { + // This should be overwritten by most field implementations. + let half = Self::from_prime_subfield( + Self::PrimeSubfield::TWO + .try_inverse() + .expect("Cannot divide by 2 in fields with characteristic 2"), + ); + *self * half + } + + /// Divide by a given power of two. `div_2exp_u64(a, exp) = a/2^exp` + /// + /// # Panics + /// The function will panic if the field has characteristic 2. + #[must_use] + #[inline] + fn div_2exp_u64(&self, exp: u64) -> Self { + // This should be overwritten by most field implementations. + *self + * Self::from_prime_subfield( + Self::PrimeSubfield::TWO + .try_inverse() + .expect("Cannot divide by 2 in fields with characteristic 2") + .exp_u64(exp), + ) + } + + /// Add two slices of field elements together, returning the result in the first slice. + /// + /// Makes use of packing to speed up the addition. + /// + /// This is optimal for cases where the two slices are small to medium length. E.g. between + /// `F::Packing::WIDTH` and roughly however many elements fit in a cache line. + /// + /// For larger slices, it's likely worthwhile to use parallelization before calling this. + /// Similarly if you need to add a large number of slices together, it's best to + /// break them into small chunks and call this on the smaller chunks. + /// + /// # Panics + /// The function will panic if the lengths of the two slices are not equal. + #[inline] + fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) { + let (shorts_1, suffix_1) = Self::Packing::pack_slice_with_suffix_mut(slice_1); + let (shorts_2, suffix_2) = Self::Packing::pack_slice_with_suffix(slice_2); + debug_assert_eq!(shorts_1.len(), shorts_2.len()); + debug_assert_eq!(suffix_1.len(), suffix_2.len()); + for (x_1, &x_2) in shorts_1.iter_mut().zip(shorts_2) { + *x_1 += x_2; + } + for (x_1, &x_2) in suffix_1.iter_mut().zip(suffix_2) { + *x_1 += x_2; + } + } + + /// The number of elements in the field. + /// + /// This will either be prime if the field is a PrimeField or a power of a + /// prime if the field is an extension field. + #[must_use] + fn order() -> BigUint; + + /// The number of bits required to define an element of this field. + /// + /// Usually due to storage and practical reasons the memory size of + /// a field element will be a little larger than bits(). + #[must_use] + #[inline] + fn bits() -> usize { + Self::order().bits() as usize + } +} + +/// A field isomorphic to `ℤ/p` for some prime `p`. +/// +/// There is a natural map from `ℤ` to `ℤ/p` which sends an integer `r` to its conjugacy class `[r]`. +/// Canonically, each conjugacy class `[r]` can be represented by the unique integer `s` in `[0, p - 1)` +/// satisfying `s = r mod p`. This however is often not the most convenient computational representation +/// and so internal representations of field elements might differ from this and may change over time. +pub trait PrimeField: + Field + + Ord + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap + + QuotientMap +{ + /// Return the representative of `value` in canonical form + /// which lies in the range `0 <= x < self.order()`. + #[must_use] + fn as_canonical_biguint(&self) -> BigUint; +} + +/// A prime field `ℤ/p` with order, `p < 2^64`. +pub trait PrimeField64: PrimeField { + const ORDER_U64: u64; + + /// Return the representative of `value` in canonical form + /// which lies in the range `0 <= x < ORDER_U64`. + #[must_use] + fn as_canonical_u64(&self) -> u64; + + /// Convert a field element to a `u64` such that any two field elements + /// are converted to the same `u64` if and only if they represent the same value. + /// + /// This will be the fastest way to convert a field element to a `u64` and + /// is intended for use in hashing. It will also be consistent across different targets. + #[must_use] + #[inline(always)] + fn to_unique_u64(&self) -> u64 { + // A simple default which is optimal for some fields. + self.as_canonical_u64() + } +} + +/// A prime field `ℤ/p` with order `p < 2^32`. +pub trait PrimeField32: PrimeField64 { + const ORDER_U32: u32; + + /// Return the representative of `value` in canonical form + /// which lies in the range `0 <= x < ORDER_U64`. + #[must_use] + fn as_canonical_u32(&self) -> u32; + + /// Convert a field element to a `u32` such that any two field elements + /// are converted to the same `u32` if and only if they represent the same value. + /// + /// This will be the fastest way to convert a field element to a `u32` and + /// is intended for use in hashing. It will also be consistent across different targets. + #[must_use] + #[inline(always)] + fn to_unique_u32(&self) -> u32 { + // A simple default which is optimal for some fields. + self.as_canonical_u32() + } +} + +/// A field `EF` which is also an algebra over a field `F`. +/// +/// This provides a couple of convenience methods on top of the +/// standard methods provided by `Field`, `Algebra` and `BasedVectorSpace`. +/// +/// It also provides a type which handles packed vectors of extension field elements. +pub trait ExtensionField: Field + Algebra + BasedVectorSpace { + type ExtensionPacking: PackedFieldExtension + 'static + Copy + Send + Sync; + + /// Determine if the given element lies in the base field. + #[must_use] + fn is_in_basefield(&self) -> bool; + + /// If the element lies in the base field project it down. + /// Otherwise return None. + #[must_use] + fn as_base(&self) -> Option; +} + +// Every field is trivially a one dimensional extension over itself. +impl ExtensionField for F { + type ExtensionPacking = F::Packing; + + #[inline] + fn is_in_basefield(&self) -> bool { + true + } + + #[inline] + fn as_base(&self) -> Option { + Some(*self) + } +} + +/// A field which supplies information like the two-adicity of its multiplicative group, and methods +/// for obtaining two-adic generators. +pub trait TwoAdicField: Field { + /// The number of factors of two in this field's multiplicative group. + const TWO_ADICITY: usize; + + /// Returns a generator of the multiplicative group of order `2^bits`. + /// Assumes `bits <= TWO_ADICITY`, otherwise the result is undefined. + #[must_use] + fn two_adic_generator(bits: usize) -> Self; +} + +/// An iterator which returns the powers of a base element `b` shifted by current `c`: `c, c * b, c * b^2, ...`. +#[derive(Clone, Debug)] +pub struct Powers { + pub base: F, + pub current: F, +} + +impl Iterator for Powers { + type Item = R; + + fn next(&mut self) -> Option { + let result = self.current.clone(); + self.current *= self.base.clone(); + Some(result) + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/helpers.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/helpers.rs new file mode 100644 index 00000000..582770a8 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/helpers.rs @@ -0,0 +1,184 @@ +use alloc::vec::Vec; +use core::iter::Sum; +use core::mem::MaybeUninit; +use core::ops::Mul; + +use p3_maybe_rayon::prelude::*; + +use crate::field::Field; +use crate::{PackedValue, PrimeCharacteristicRing, PrimeField, PrimeField32}; + +/// Computes a multiplicative subgroup whose order is known in advance. +pub fn cyclic_subgroup_known_order( + generator: F, + order: usize, +) -> impl Iterator + Clone { + generator.powers().take(order) +} + +/// Computes a coset of a multiplicative subgroup whose order is known in advance. +pub fn cyclic_subgroup_coset_known_order( + generator: F, + shift: F, + order: usize, +) -> impl Iterator + Clone { + generator.shifted_powers(shift).take(order) +} + +/// Scales each element of the slice by `s` using packing. +/// +/// # Performance +/// For large slices, use [`par_scale_slice_in_place`]. +/// +/// # Deprecation +/// This function will be replaced with [`scale_slice_in_place`] whose semantics and arguments +/// will be the same as this one. +pub fn scale_slice_in_place_single_core(slice: &mut [F], s: F) { + let (packed, sfx) = F::Packing::pack_slice_with_suffix_mut(slice); + let packed_s: F::Packing = s.into(); + packed.iter_mut().for_each(|x| *x *= packed_s); + sfx.iter_mut().for_each(|x| *x *= s); +} + +/// Scales each element of the slice by `s` using packing and parallelization. +/// +/// # Performance +/// For small slices, use [`scale_slice_in_place_single_core`]. +/// Requires the `parallel` feature. +#[inline] +pub fn par_scale_slice_in_place(slice: &mut [F], s: F) { + let (packed, sfx) = F::Packing::pack_slice_with_suffix_mut(slice); + let packed_s: F::Packing = s.into(); + packed.par_iter_mut().for_each(|x| *x *= packed_s); + sfx.iter_mut().for_each(|x| *x *= s); +} + +/// This function is deprecated. It is currently a wrapper for [`par_scale_slice_in_place`], which +/// it should be replaced with if parallelization is required. +#[deprecated( + note = "use `par_scale_slice_in_place` instead which will replace this method in the future" +)] +pub fn scale_slice_in_place(s: F, slice: &mut [F]) { + par_scale_slice_in_place(slice, s) +} + +/// Adds `other`, scaled by `s`, to the mutable `slice` using packing, or `slice += other * s`. +/// +/// # Performance +/// For large slices, use [`par_add_scaled_slice_in_place`]. +pub fn add_scaled_slice_in_place(slice: &mut [F], other: &[F], s: F) { + debug_assert_eq!(slice.len(), other.len(), "slices must have equal length"); + let (slice_packed, slice_sfx) = F::Packing::pack_slice_with_suffix_mut(slice); + let (other_packed, other_sfx) = F::Packing::pack_slice_with_suffix(other); + let packed_s: F::Packing = s.into(); + slice_packed + .iter_mut() + .zip(other_packed) + .for_each(|(x, y)| *x += *y * packed_s); + slice_sfx + .iter_mut() + .zip(other_sfx) + .for_each(|(x, y)| *x += *y * s); +} + +/// Adds `other`, scaled by `s`, to the mutable `slice` using packing, or `slice += other * s`. +/// +/// # Performance +/// For small slices, use [`add_scaled_slice_in_place`]. +/// Requires the `parallel` feature. +pub fn par_add_scaled_slice_in_place(slice: &mut [F], other: &[F], s: F) { + debug_assert_eq!(slice.len(), other.len(), "slices must have equal length"); + let (slice_packed, slice_sfx) = F::Packing::pack_slice_with_suffix_mut(slice); + let (other_packed, other_sfx) = F::Packing::pack_slice_with_suffix(other); + let packed_s: F::Packing = s.into(); + slice_packed + .par_iter_mut() + .zip(other_packed.par_iter()) + .for_each(|(x, y)| *x += *y * packed_s); + slice_sfx + .iter_mut() + .zip(other_sfx) + .for_each(|(x, y)| *x += *y * s); +} + +/// Extend a ring `R` element `x` to an array of length `D` +/// by filling zeros. +#[inline] +pub const fn field_to_array(x: R) -> [R; D] { + let mut arr = [const { MaybeUninit::uninit() }; D]; + arr[0] = MaybeUninit::new(x); + let mut i = 1; + while i < D { + arr[i] = MaybeUninit::new(R::ZERO); + i += 1; + } + unsafe { core::mem::transmute_copy::<_, [R; D]>(&arr) } +} + +/// Given an element x from a 32 bit field F_P compute x/2. +#[inline] +pub const fn halve_u32(x: u32) -> u32 { + let shift = (P + 1) >> 1; + let half = x >> 1; + if x & 1 == 0 { half } else { half + shift } +} + +/// Given an element x from a 64 bit field F_P compute x/2. +#[inline] +pub const fn halve_u64(x: u64) -> u64 { + let shift = (P + 1) >> 1; + let half = x >> 1; + if x & 1 == 0 { half } else { half + shift } +} + +/// Reduce a slice of 32-bit field elements into a single element of a larger field. +/// +/// Uses base-$2^{32}$ decomposition: +/// +/// ```math +/// \begin{equation} +/// \text{reduce\_32}(vals) = \sum_{i=0}^{n-1} a_i \cdot 2^{32i} +/// \end{equation} +/// ``` +pub fn reduce_32(vals: &[SF]) -> TF { + // If the characteristic of TF is > 2^64, from_int and from_canonical_unchecked act identically + let base = TF::from_int(1u64 << 32); + vals.iter().rev().fold(TF::ZERO, |acc, val| { + acc * base + TF::from_int(val.as_canonical_u32()) + }) +} + +/// Split a large field element into `n` base-$2^{64}$ chunks and map each into a 32-bit field. +/// +/// Converts: +/// ```math +/// \begin{equation} +/// x = \sum_{i=0}^{n-1} d_i \cdot 2^{64i} +/// \end{equation} +/// ``` +/// +/// Pads with zeros if needed. +pub fn split_32(val: SF, n: usize) -> Vec { + let mut result: Vec = val + .as_canonical_biguint() + .to_u64_digits() + .iter() + .take(n) + .map(|d| TF::from_u64(*d)) + .collect(); + + // Pad with zeros if needed + result.resize_with(n, || TF::ZERO); + result +} + +/// Maximally generic dot product. +pub fn dot_product(li: LI, ri: RI) -> S +where + LI: Iterator, + RI: Iterator, + LI::Item: Mul, + S: Sum<>::Output>, +{ + li.zip(ri).map(|(l, r)| l * r).sum() +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/integers.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/integers.rs new file mode 100644 index 00000000..4b9a71fb --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/integers.rs @@ -0,0 +1,607 @@ +//! A collection of traits and macros which convert primitive integer types into field elements. + +/// A macro which lets us define the function `from_Int` +/// where `Int` can be replaced by any integer type. +/// +/// Running, `from_integer_types!(Int)` adds the following code to a trait: +/// +/// ```rust,ignore +/// /// Given an integer `r`, return the sum of `r` copies of `ONE`: +/// /// +/// /// `r * Self::ONE = Self::ONE + ... + Self::ONE (r times)`. +/// /// +/// /// Note that the output only depends on `r mod p`. +/// /// +/// /// This should be avoided in performance critical locations. +/// fn from_Int(int: Int) -> Self { +/// Self::from_prime_subfield(Self::PrimeSubfield::from_int(int)) +/// } +/// ``` +/// +/// This macro can be run for any `Int` where `Self::PrimeSubfield` implements `QuotientMap`. +/// It considerably cuts down on the amount of copy/pasted code. +macro_rules! from_integer_types { + ($($type:ty),* $(,)? ) => { + $( paste::paste!{ + /// Given an integer `r`, return the sum of `r` copies of `ONE`: + /// + /// `r * Self::ONE = Self::ONE + ... + Self::ONE (r times)`. + /// + /// Note that the output only depends on `r mod p`. + /// + /// This should be avoided in performance critical locations. + fn [](int: $type) -> Self { + Self::from_prime_subfield(Self::PrimeSubfield::from_int(int)) + } + } + )* + }; +} + +pub(crate) use from_integer_types; + +/// Implementation of the quotient map `ℤ -> ℤ/p` which sends an integer `r` to its conjugacy class `[r]`. +/// +/// This is the key trait allowing us to convert integers into field elements. Each prime field +/// should implement this for all primitive integer types. +pub trait QuotientMap: Sized { + /// Convert a given integer into an element of the field `ℤ/p`. + /// + /// This is the most generic method which makes no assumptions on the size of the input. + /// Where possible, this method should be used with the smallest possible integer type. + /// For example, if a 32-bit integer `x` is known to be less than `2^16`, then + /// `from_int(x as u16)` will often be faster than `from_int(x)`. + /// + /// This method is also strongly preferred over `from_canonical_checked/from_canonical_unchecked`. + /// It will usually be identical when `Int` is a small type, e.g. `u8/u16` and is safer for + /// larger types. + fn from_int(int: Int) -> Self; + + /// Convert a given integer into an element of the field `ℤ/p`. The input is checked to + /// ensure it lies within a given range. + /// - If `Int` is an unsigned integer type the input must lie in `[0, p - 1]`. + /// - If `Int` is a signed integer type the input must lie in `[-(p - 1)/2, (p - 1)/2]`. + /// + /// Return `None` if the input lies outside this range and `Some(val)` otherwise. + fn from_canonical_checked(int: Int) -> Option; + + /// Convert a given integer into an element of the field `ℤ/p`. The input is guaranteed + /// to lie within a specific range depending on `p`. If the input lies outside of this + /// range, the output is undefined. + /// + /// In general `from_canonical_unchecked` will be faster for either `signed` or `unsigned` + /// types but the specifics will depend on the field. + /// + /// # Safety + /// - If `Int` is an unsigned integer type then the allowed range will include `[0, p - 1]`. + /// - If `Int` is a signed integer type then the allowed range will include `[-(p - 1)/2, (p - 1)/2]`. + unsafe fn from_canonical_unchecked(int: Int) -> Self; +} + +/// This allows us to avoid some duplication which arises when working with fields which contain a generic parameter. +/// +/// See `quotient_map_small_int` to see what this will expand to/how to call it. This is not intended for use outside of +/// that macro. +#[macro_export] +macro_rules! quotient_map_small_internals { + ($field:ty, $field_size:ty, $small_int:ty) => { + #[doc = concat!("Convert a given `", stringify!($small_int), "` integer into an element of the `", stringify!($field), "` field. + \n Due to the integer type, the input value is always canonical.")] + #[inline] + fn from_int(int: $small_int) -> Self { + // Should be removed by the compiler. + assert!(size_of::<$small_int>() < size_of::<$field_size>()); + unsafe { + Self::from_canonical_unchecked(int as $field_size) + } + } + + #[doc = concat!("Convert a given `", stringify!($small_int), "` integer into an element of the `", stringify!($field), "` field. + \n Due to the integer type, the input value is always canonical.")] + #[inline] + fn from_canonical_checked(int: $small_int) -> Option { + // Should be removed by the compiler. + assert!(size_of::<$small_int>() < size_of::<$field_size>()); + Some(unsafe { + Self::from_canonical_unchecked(int as $field_size) + }) + } + + #[doc = concat!("Convert a given `", stringify!($small_int), "` integer into an element of the `", stringify!($field), "` field. + \n Due to the integer type, the input value is always canonical.")] + #[inline] + unsafe fn from_canonical_unchecked(int: $small_int) -> Self { + // We use debug_assert to ensure this is removed by the compiler in release mode. + debug_assert!(size_of::<$small_int>() < size_of::<$field_size>()); + unsafe { + Self::from_canonical_unchecked(int as $field_size) + } + } + }; +} + +/// If the integer type is smaller than the field order all possible inputs are canonical. +/// In such a case we can easily implement `QuotientMap` as all three methods will coincide. +/// +/// The range of acceptable integer types depends on the size of the field: +/// - For 31 bit fields, `SmallInt = u8, u16, i8, i16`. +/// - For 64 bit fields, `SmallInt = u8, u16, u32, i8, i16, i32`. +/// - For large fields (E.g. `Bn254`), `SmallInt` can be anything except for the largest primitive integer type `u128/i128` +/// +/// This macro accepts 3 inputs. +/// - The name of the prime field `P` +/// - The larger integer type `Int` which inputs should be cast to. +/// - A list of smaller integer types to auto implement `QuotientMap`. +/// +/// Then `from_int`, `from_canonical_checked`, `from_canonical_unchecked` are all +/// implemented by casting the input to an `Int` and using the `from_canonical_unchecked` +/// method from `QuotientMap`. +/// +/// For a concrete example, `quotient_map_small_int!(Mersenne31, u32, [u8])` produces the following code: +/// +/// ```rust,ignore +/// impl QuotientMap for Mersenne31 { +/// /// Convert a given `u8` integer into an element of the `Mersenne31` field. +/// /// +/// /// Due to the integer type, the input value is always canonical. +/// #[inline] +/// fn from_int(int: u8) -> Mersenne31 { +/// // Should be removed by the compiler. +/// assert!(size_of::() < size_of::()); +/// unsafe { +/// Self::from_canonical_unchecked(int as u32) +/// } +/// } +/// +/// /// Convert a given `u8` integer into an element of the `Mersenne31` field. +/// /// +/// /// Due to the integer type, the input value is always canonical. +/// #[inline] +/// fn from_canonical_checked(int: u8) -> Option { +/// // Should be removed by the compiler. +/// assert!(size_of::() < size_of::()); +/// Some(unsafe { +/// Self::from_canonical_unchecked(int as u32) +/// }) +/// } +/// +/// /// Convert a given `u8` integer into an element of the `Mersenne31` field. +/// /// +/// /// Due to the integer type, the input value is always canonical. +/// #[inline] +/// unsafe fn from_canonical_unchecked(int: u8) -> Mersenne31 { +/// // We use debug_assert to ensure this is removed by the compiler in release mode. +/// debug_assert!(size_of::() < size_of::()); +/// unsafe { +/// Self::from_canonical_unchecked(int as u32) +/// } +/// } +/// } +///``` +/// +/// Fields will often use this method twice. Once for unsigned ints and once for signed ints. +/// +/// We need two slightly different versions for this macro as MontyField31 uses generic parameters. +#[macro_export] +macro_rules! quotient_map_small_int { + ($field:ty, $field_size:ty, [$($small_int:ty),*] ) => { + $( + paste::paste!{ + impl QuotientMap<$small_int> for $field { + $crate::quotient_map_small_internals!($field, $field_size, $small_int); + } + } + )* + }; + + ($field:ty, $field_size:ty, $field_param:ty, [$($small_int:ty),*] ) => { + $( + paste::paste!{ + impl QuotientMap<$small_int> for $field { + $crate::quotient_map_small_internals!($field, $field_size, $small_int); + } + } + )* + }; +} + +/// If the unsigned integer type is large enough, there is often no method better for `from_int` than +/// just doing a modular reduction to a smaller type. +/// +/// This macro accepts 6 inputs. +/// - The name of the prime field `P` +/// - The smallest natural integer type large enough to contain the field characteristic. +/// - The characteristic of the field. +/// - A string giving the range for which from_canonical_checked produces the correct result. +/// - A string giving the range for which from_canonical_unchecked produces the correct result. +/// - A list of large integer types to auto implement `QuotientMap`. +/// +/// For a concrete example, `quotient_map_large_uint!(Mersenne31, u32, Mersenne31::ORDER_U32, "`\[0, 2^31 - 2\]`", "`\[0, 2^31 - 1\]`", [u128])` would produce the following code: +/// +/// ```rust,ignore +/// impl QuotientMap for Mersenne31 { +/// /// Convert a given `u128` integer into an element of the `Mersenne31` field. +/// /// +/// /// Uses a modular reduction to reduce to canonical form. +/// /// This should be avoided in performance critical locations. +/// #[inline] +/// fn from_int(int: u128) -> Mersenne31 { +/// // Should be removed by the compiler. +/// assert!(size_of::() > size_of::()); +/// let red = (int % (Mersenne31::ORDER_U32 as u128)) as u32; +/// unsafe { +/// // This is safe as red is less than the field order by assumption. +/// Self::from_canonical_unchecked(red) +/// } +/// } +/// +/// /// Convert a given `u128` integer into an element of the `Mersenne31` field. +/// /// +/// /// Returns `None` if the input does not lie in the range: [0, 2^31 - 2]. +/// #[inline] +/// fn from_canonical_checked(int: u128) -> Option { +/// if int < Mersenne31::ORDER_U32 as u128 { +/// unsafe { +/// // This is safe as we just checked that int is less than the field order. +/// Some(Self::from_canonical_unchecked(int as u32)) +/// } +/// } else { +/// None +/// } +/// } +/// +/// /// Convert a given `u128` integer into an element of the `Mersenne31` field. +/// /// +/// /// # Safety +/// /// The input must lie in the range:", [0, 2^31 - 1]. +/// #[inline] +/// unsafe fn from_canonical_unchecked(int: u128) -> Mersenne31 { +/// unsafe { +/// Self::from_canonical_unchecked(int as u32) +/// } +/// } +/// } +///``` +#[macro_export] +macro_rules! quotient_map_large_uint { + ($field:ty, $field_size:ty, $field_order:expr, $checked_bounds:literal, $unchecked_bounds:literal, [$($large_int:ty),*] ) => { + $( + impl QuotientMap<$large_int> for $field { + #[doc = concat!("Convert a given `", stringify!($large_int), "` integer into an element of the `", stringify!($field), "` field. + \n Uses a modular reduction to reduce to canonical form. \n This should be avoided in performance critical locations.")] + #[inline] + fn from_int(int: $large_int) -> $field { + assert!(size_of::<$large_int>() > size_of::<$field_size>()); + let red = (int % ($field_order as $large_int)) as $field_size; + unsafe { + // This is safe as red is less than the field order by assumption. + Self::from_canonical_unchecked(red) + } + } + + #[doc = concat!("Convert a given `", stringify!($large_int), "` integer into an element of the `", stringify!($field), "` field. + \n Returns `None` if the input does not lie in the range:", $checked_bounds, ".")] + #[inline] + fn from_canonical_checked(int: $large_int) -> Option<$field> { + if int < $field_order as $large_int { + unsafe { + // This is safe as we just checked that int is less than the field order. + Some(Self::from_canonical_unchecked(int as $field_size)) + } + } else { + None + } + } + + #[doc = concat!("Convert a given `", stringify!($large_int), "` integer into an element of the `", stringify!($field), "` field.")] + /// + /// # Safety + #[doc = concat!("The input must lie in the range:", $unchecked_bounds, ".")] + #[inline] + unsafe fn from_canonical_unchecked(int: $large_int) -> $field { + unsafe { + Self::from_canonical_unchecked(int as $field_size) + } + } + } + )* + }; +} + +/// For large signed integer types, a simple method which is usually good enough is to simply check the sign and use this to +/// pass to the equivalent unsigned method. +/// +/// This will often not be the fastest implementation but should be good enough for most cases. +/// +/// This macro accepts 4 inputs. +/// - The name of the prime field `P`. +/// - The smallest natural integer type large enough to contain the field characteristic. +/// - A string giving the range for which from_canonical_checked produces the correct result. +/// - A string giving the range for which from_canonical_unchecked produces the correct result. +/// - A list of pairs of large sign and unsigned integer types to auto implement `QuotientMap`. +/// +/// For a concrete example, `quotient_map_large_iint!(Mersenne31, i32, "`\[-2^30, 2^30\]`", "`\[1 - 2^31, 2^31 - 1\]`", [(i128, u128)])` would produce the following code: +/// +/// ```rust,ignore +/// impl QuotientMap for Mersenne31 { +/// /// Convert a given `i128` integer into an element of the `Mersenne31` field. +/// /// +/// /// This checks the sign and then makes use of the equivalent method for unsigned integers. +/// /// This should be avoided in performance critical locations. +/// #[inline] +/// fn from_int(int: i128) -> Mersenne31 { +/// if int >= 0 { +/// Self::from_int(int as u128) +/// } else { +/// -Self::from_int(-int as u128) +/// } +/// } +/// +/// /// Convert a given `i128` integer into an element of the `Mersenne31` field. +/// /// +/// /// Returns `None` if the input does not lie in the range: `[-2^30, 2^30]`. +/// #[inline] +/// fn from_canonical_checked(int: i128) -> Option { +/// // We just check that int fits into an i32 now and then use the i32 method. +/// let int_small = TryInto::::try_into(int); +/// if int_small.is_ok() { +/// Self::from_canonical_checked(int_small.unwrap()) +/// } else { +/// None +/// } +/// } +/// +/// /// Convert a given `i128` integer into an element of the `Mersenne31` field. +/// /// +/// /// # Safety +/// /// The input must lie in the range:", `[1 - 2^31, 2^31 - 1]`. +/// #[inline] +/// unsafe fn from_canonical_unchecked(int: i128) -> Mersenne31 { +/// unsafe { +/// Self::from_canonical_unchecked(int as i32) +/// } +/// } +/// } +///``` +#[macro_export] +macro_rules! quotient_map_large_iint { + ($field:ty, $field_size:ty, $checked_bounds:literal, $unchecked_bounds:literal, [$(($large_signed_int:ty, $large_int:ty)),*] ) => { + $( + impl QuotientMap<$large_signed_int> for $field { + #[doc = concat!("Convert a given `", stringify!($large_signed_int), "` integer into an element of the `", stringify!($field), "` field. + \n This checks the sign and then makes use of the equivalent method for unsigned integers. \n This should be avoided in performance critical locations.")] + #[inline] + fn from_int(int: $large_signed_int) -> $field { + if int >= 0 { + Self::from_int(int as $large_int) + } else { + -Self::from_int(-int as $large_int) + } + } + + #[doc = concat!("Convert a given `", stringify!($large_int), "` integer into an element of the `", stringify!($field), "` field. + \n Returns `None` if the input does not lie in the range:", $checked_bounds, ".")] + #[inline] + fn from_canonical_checked(int: $large_signed_int) -> Option<$field> { + let int_small = TryInto::<$field_size>::try_into(int).ok(); + + // The type of the following is Option>. + // We use the ? operator to convert it to Option<$field>, with + // None and Some(None) both becoming None. + int_small.map(Self::from_canonical_checked)? + } + + #[doc = concat!("Convert a given `", stringify!($large_int), "` integer into an element of the `", stringify!($field), "` field.")] + /// + /// # Safety + #[doc = concat!("The input must lie in the range:", $unchecked_bounds, ".")] + #[inline] + unsafe fn from_canonical_unchecked(int: $large_signed_int) -> $field { + unsafe { + Self::from_canonical_unchecked(int as $field_size) + } + } + } + )* + }; +} + +/// We implement `QuotientMap` (`QuotientMap`) by matching against the size of `usize` (`isize`) +/// and then converting `usize` (`isize`) into the equivalent matching integer type. +/// +/// The code is identical for both `usize` and `isize` outside of replacing some u's by i's so we use a macro +/// to avoid the copy and paste. +macro_rules! impl_u_i_size { + ($intsize:ty, $int8:ty, $int16:ty, $int32:ty, $int64:ty, $int128:ty) => { + impl< + F: QuotientMap<$int8> + + QuotientMap<$int16> + + QuotientMap<$int32> + + QuotientMap<$int64> + + QuotientMap<$int128>, + > QuotientMap<$intsize> for F + { + #[doc = concat!("We use the `from_int` method of the primitive integer type identical to `", stringify!($intsize), "` on this machine")] + fn from_int(int: $intsize) -> Self { + match size_of::<$intsize>() { + 1 => Self::from_int(int as $int8), + 2 => Self::from_int(int as $int16), + 4 => Self::from_int(int as $int32), + 8 => Self::from_int(int as $int64), + 16 => Self::from_int(int as $int128), + _ => unreachable!(concat!(stringify!($intsize), "is not equivalent to any primitive integer types.")), + } + } + + #[doc = concat!("We use the `from_canonical_checked` method of the primitive integer type identical to `", stringify!($intsize), "` on this machine")] + fn from_canonical_checked(int: $intsize) -> Option { + match size_of::<$intsize>() { + 1 => Self::from_canonical_checked(int as $int8), + 2 => Self::from_canonical_checked(int as $int16), + 4 => Self::from_canonical_checked(int as $int32), + 8 => Self::from_canonical_checked(int as $int64), + 16 => Self::from_canonical_checked(int as $int128), + _ => unreachable!(concat!(stringify!($intsize), " is not equivalent to any primitive integer types.")), + } + } + + #[doc = concat!("We use the `from_canonical_unchecked` method of the primitive integer type identical to `", stringify!($intsize), "` on this machine")] + unsafe fn from_canonical_unchecked(int: $intsize) -> Self { + unsafe { + match size_of::<$intsize>() { + 1 => Self::from_canonical_unchecked(int as $int8), + 2 => Self::from_canonical_unchecked(int as $int16), + 4 => Self::from_canonical_unchecked(int as $int32), + 8 => Self::from_canonical_unchecked(int as $int64), + 16 => Self::from_canonical_unchecked(int as $int128), + _ => unreachable!(concat!(stringify!($intsize), " is not equivalent to any primitive integer types.")), + } + } + } + } + }; +} + +impl_u_i_size!(usize, u8, u16, u32, u64, u128); +impl_u_i_size!(isize, i8, i16, i32, i64, i128); + +/// A simple macro which allows us to implement the `RawSerializable` trait for any 32-bit field. +/// The field must implement PrimeField32. +/// +/// This macro doesn't need any inputs as the implementation is identical for all 32-bit fields. +#[macro_export] +macro_rules! impl_raw_serializable_primefield32 { + () => { + const NUM_BYTES: usize = 4; + + #[allow(refining_impl_trait)] + #[inline] + fn into_bytes(self) -> [u8; 4] { + self.to_unique_u32().to_le_bytes() + } + + #[inline] + fn into_u32_stream(input: impl IntoIterator) -> impl IntoIterator { + // As every element is 32 bits, we can just convert the input to a unique u32. + input.into_iter().map(|x| x.to_unique_u32()) + } + + #[inline] + fn into_u64_stream(input: impl IntoIterator) -> impl IntoIterator { + let mut input = input.into_iter(); + iter::from_fn(move || { + // If the first input.next() returns None, we return None. + let a = input.next()?; + // Otherwise we either pack 2 32 bit elements together if the iterator + // gives a second value or just cast the 32 bit element to 64 bits. + if let Some(b) = input.next() { + Some(a.to_unique_u64() | b.to_unique_u64() << 32) + } else { + Some(a.to_unique_u64()) + } + }) + } + + #[inline] + fn into_parallel_byte_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + input.into_iter().flat_map(|vector| { + let bytes = vector.map(|elem| elem.into_bytes()); + (0..Self::NUM_BYTES).map(move |i| array::from_fn(|j| bytes[j][i])) + }) + } + + #[inline] + fn into_parallel_u32_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + // As every element is 32 bits, we can just convert the input to a unique u32. + input.into_iter().map(|vec| vec.map(|x| x.to_unique_u32())) + } + + #[inline] + fn into_parallel_u64_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + let mut input = input.into_iter(); + iter::from_fn(move || { + // If the first input.next() returns None, we return None. + let a = input.next()?; + // Otherwise we either pack pairs of 32 bit elements together if the iterator + // gives two arrays of or just cast the 32 bit elements to 64 bits. + if let Some(b) = input.next() { + let ab = array::from_fn(|i| { + let ai = a[i].to_unique_u64(); + let bi = b[i].to_unique_u64(); + ai | (bi << 32) + }); + Some(ab) + } else { + Some(a.map(|x| x.to_unique_u64())) + } + }) + } + }; +} + +/// A simple macro which allows us to implement the `RawSerializable` trait for any 64-bit field. +/// The field must implement PrimeField64 (and should not implement PrimeField32). +/// +/// This macro doesn't need any inputs as the implementation is identical for all 64-bit fields. +#[macro_export] +macro_rules! impl_raw_serializable_primefield64 { + () => { + const NUM_BYTES: usize = 8; + + #[allow(refining_impl_trait)] + #[inline] + fn into_bytes(self) -> [u8; 8] { + self.to_unique_u64().to_le_bytes() + } + + #[inline] + fn into_u32_stream(input: impl IntoIterator) -> impl IntoIterator { + input.into_iter().flat_map(|x| { + let x_u64 = x.to_unique_u64(); + [x_u64 as u32, (x_u64 >> 32) as u32] + }) + } + + #[inline] + fn into_u64_stream(input: impl IntoIterator) -> impl IntoIterator { + // As every element is 64 bits, we can just convert the input to a unique u64. + input.into_iter().map(|x| x.to_unique_u64()) + } + + #[inline] + fn into_parallel_byte_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + input.into_iter().flat_map(|vector| { + let bytes = vector.map(|elem| elem.into_bytes()); + (0..Self::NUM_BYTES).map(move |i| array::from_fn(|j| bytes[j][i])) + }) + } + + #[inline] + fn into_parallel_u32_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + input.into_iter().flat_map(|vec| { + let vec_64 = vec.map(|x| x.to_unique_u64()); + let vec_32_lo = vec_64.map(|x| x as u32); + let vec_32_hi = vec_64.map(|x| (x >> 32) as u32); + [vec_32_lo, vec_32_hi] + }) + } + + #[inline] + fn into_parallel_u64_streams( + input: impl IntoIterator, + ) -> impl IntoIterator { + // As every element is 64 bits, we can just convert the input to a unique u64. + input.into_iter().map(|vec| vec.map(|x| x.to_unique_u64())) + } + }; +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/lib.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/lib.rs new file mode 100644 index 00000000..dbd61978 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/lib.rs @@ -0,0 +1,21 @@ +//! A framework for finite fields. + +#![no_std] + +extern crate alloc; + +mod array; +mod batch_inverse; +pub mod coset; +pub mod exponentiation; +pub mod extension; +mod field; +mod helpers; +pub mod integers; +mod packed; + +pub use array::*; +pub use batch_inverse::*; +pub use field::*; +pub use helpers::*; +pub use packed::*; diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/packed.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/packed.rs new file mode 100644 index 00000000..0d2bd7ab --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/src/packed.rs @@ -0,0 +1,373 @@ +use alloc::vec::Vec; +use core::mem::MaybeUninit; +use core::ops::Div; +use core::{array, slice}; + +use crate::field::Field; +use crate::{Algebra, BasedVectorSpace, ExtensionField, Powers, PrimeCharacteristicRing}; + +/// A trait to constrain types that can be packed into a packed value. +/// +/// The `Packable` trait allows us to specify implementations for potentially conflicting types. +pub trait Packable: 'static + Default + Copy + Send + Sync + PartialEq + Eq {} + +/// A trait for array-like structs made up of multiple scalar elements. +/// +/// # Safety +/// - If `P` implements `PackedField` then `P` must be castable to/from `[P::Value; P::WIDTH]` +/// without UB. +pub unsafe trait PackedValue: 'static + Copy + Send + Sync { + /// The scalar type that is packed into this value. + type Value: Packable; + + /// Number of scalar values packed together. + const WIDTH: usize; + + /// Interprets a slice of scalar values as a packed value reference. + /// + /// # Panics: + /// This function will panic if `slice.len() != Self::WIDTH` + fn from_slice(slice: &[Self::Value]) -> &Self; + + /// Interprets a mutable slice of scalar values as a mutable packed value. + /// + /// # Panics: + /// This function will panic if `slice.len() != Self::WIDTH` + fn from_slice_mut(slice: &mut [Self::Value]) -> &mut Self; + + /// Constructs a packed value using a function to generate each element. + /// + /// Similar to `core:array::from_fn`. + fn from_fn(f: F) -> Self + where + F: FnMut(usize) -> Self::Value; + + /// Returns the underlying scalar values as an immutable slice. + fn as_slice(&self) -> &[Self::Value]; + + /// Returns the underlying scalar values as a mutable slice. + fn as_slice_mut(&mut self) -> &mut [Self::Value]; + + /// Packs a slice of scalar values into a slice of packed values. + /// + /// # Panics + /// Panics if the slice length is not divisible by `WIDTH`. + fn pack_slice(buf: &[Self::Value]) -> &[Self] { + // Sources vary, but this should be true on all platforms we care about. + // This should be a const assert, but trait methods can't access `Self` in a const context, + // even with inner struct instantiation. So we will trust LLVM to optimize this out. + assert!(align_of::() <= align_of::()); + assert!( + buf.len() % Self::WIDTH == 0, + "Slice length (got {}) must be a multiple of packed field width ({}).", + buf.len(), + Self::WIDTH + ); + let buf_ptr = buf.as_ptr().cast::(); + let n = buf.len() / Self::WIDTH; + unsafe { slice::from_raw_parts(buf_ptr, n) } + } + + /// Packs a slice into packed values and returns the packed portion and any remaining suffix. + fn pack_slice_with_suffix(buf: &[Self::Value]) -> (&[Self], &[Self::Value]) { + let (packed, suffix) = buf.split_at(buf.len() - buf.len() % Self::WIDTH); + (Self::pack_slice(packed), suffix) + } + + /// Converts a mutable slice of scalar values into a mutable slice of packed values. + /// + /// # Panics + /// Panics if the slice length is not divisible by `WIDTH`. + fn pack_slice_mut(buf: &mut [Self::Value]) -> &mut [Self] { + assert!(align_of::() <= align_of::()); + assert!( + buf.len() % Self::WIDTH == 0, + "Slice length (got {}) must be a multiple of packed field width ({}).", + buf.len(), + Self::WIDTH + ); + let buf_ptr = buf.as_mut_ptr().cast::(); + let n = buf.len() / Self::WIDTH; + unsafe { slice::from_raw_parts_mut(buf_ptr, n) } + } + + /// Converts a mutable slice of possibly uninitialized scalar values into + /// a mutable slice of possibly uninitialized packed values. + /// + /// # Panics + /// Panics if the slice length is not divisible by `WIDTH`. + fn pack_maybe_uninit_slice_mut( + buf: &mut [MaybeUninit], + ) -> &mut [MaybeUninit] { + assert!(align_of::() <= align_of::()); + assert!( + buf.len() % Self::WIDTH == 0, + "Slice length (got {}) must be a multiple of packed field width ({}).", + buf.len(), + Self::WIDTH + ); + let buf_ptr = buf.as_mut_ptr().cast::>(); + let n = buf.len() / Self::WIDTH; + unsafe { slice::from_raw_parts_mut(buf_ptr, n) } + } + + /// Converts a mutable slice of scalar values into a pair: + /// - a slice of packed values covering the largest aligned portion, + /// - and a remainder slice of scalar values that couldn't be packed. + fn pack_slice_with_suffix_mut(buf: &mut [Self::Value]) -> (&mut [Self], &mut [Self::Value]) { + let (packed, suffix) = buf.split_at_mut(buf.len() - buf.len() % Self::WIDTH); + (Self::pack_slice_mut(packed), suffix) + } + + /// Converts a mutable slice of possibly uninitialized scalar values into a pair: + /// - a slice of possibly uninitialized packed values covering the largest aligned portion, + /// - and a remainder slice of possibly uninitialized scalar values that couldn't be packed. + fn pack_maybe_uninit_slice_with_suffix_mut( + buf: &mut [MaybeUninit], + ) -> (&mut [MaybeUninit], &mut [MaybeUninit]) { + let (packed, suffix) = buf.split_at_mut(buf.len() - buf.len() % Self::WIDTH); + (Self::pack_maybe_uninit_slice_mut(packed), suffix) + } + + /// Reinterprets a slice of packed values as a flat slice of scalar values. + /// + /// Each packed value contains `Self::WIDTH` scalar values, which are laid out + /// contiguously in memory. This function allows direct access to those scalars. + fn unpack_slice(buf: &[Self]) -> &[Self::Value] { + assert!(align_of::() >= align_of::()); + let buf_ptr = buf.as_ptr().cast::(); + let n = buf.len() * Self::WIDTH; + unsafe { slice::from_raw_parts(buf_ptr, n) } + } +} + +unsafe impl PackedValue for [T; WIDTH] { + type Value = T; + const WIDTH: usize = WIDTH; + + fn from_slice(slice: &[Self::Value]) -> &Self { + assert_eq!(slice.len(), Self::WIDTH); + slice.try_into().unwrap() + } + + fn from_slice_mut(slice: &mut [Self::Value]) -> &mut Self { + assert_eq!(slice.len(), Self::WIDTH); + slice.try_into().unwrap() + } + + fn from_fn(f: F) -> Self + where + F: FnMut(usize) -> Self::Value, + { + core::array::from_fn(f) + } + + fn as_slice(&self) -> &[Self::Value] { + self + } + + fn as_slice_mut(&mut self) -> &mut [Self::Value] { + self + } +} + +/// An array of field elements which can be packed into a vector for SIMD operations. +/// +/// # Safety +/// - See `PackedValue` above. +pub unsafe trait PackedField: Algebra + + PackedValue + // TODO: Implement packed / packed division + + Div +{ + type Scalar: Field; + + /// Construct an iterator which returns powers of `base` packed into packed field elements. + /// + /// E.g. if `Self::WIDTH = 4`, returns: `[base^0, base^1, base^2, base^3], [base^4, base^5, base^6, base^7], ...`. + #[must_use] + fn packed_powers(base: Self::Scalar) -> Powers { + Self::packed_shifted_powers(base, Self::Scalar::ONE) + } + + /// Construct an iterator which returns powers of `base` multiplied by `start` and packed into packed field elements. + /// + /// E.g. if `Self::WIDTH = 4`, returns: `[start, start*base, start*base^2, start*base^3], [start*base^4, start*base^5, start*base^6, start*base^7], ...`. + #[must_use] + fn packed_shifted_powers(base: Self::Scalar, start: Self::Scalar) -> Powers { + let mut current: Self = start.into(); + let slice = current.as_slice_mut(); + for i in 1..Self::WIDTH { + slice[i] = slice[i - 1] * base; + } + + Powers { + base: base.exp_u64(Self::WIDTH as u64).into(), + current, + } + } + + /// Compute a linear combination of a slice of base field elements and + /// a slice of packed field elements. The slices must have equal length + /// and it must be a compile time constant. + /// + /// # Panics + /// + /// May panic if the length of either slice is not equal to `N`. + fn packed_linear_combination(coeffs: &[Self::Scalar], vecs: &[Self]) -> Self { + assert_eq!(coeffs.len(), N); + assert_eq!(vecs.len(), N); + let combined: [Self; N] = array::from_fn(|i| vecs[i] * coeffs[i]); + Self::sum_array::(&combined) + } +} + +/// # Safety +/// - `WIDTH` is assumed to be a power of 2. +pub unsafe trait PackedFieldPow2: PackedField { + /// Take interpret two vectors as chunks of `block_len` elements. Unpack and interleave those + /// chunks. This is best seen with an example. If we have: + /// ```text + /// A = [x0, y0, x1, y1] + /// B = [x2, y2, x3, y3] + /// ``` + /// + /// then + /// + /// ```text + /// interleave(A, B, 1) = ([x0, x2, x1, x3], [y0, y2, y1, y3]) + /// ``` + /// + /// Pairs that were adjacent in the input are at corresponding positions in the output. + /// + /// `r` lets us set the size of chunks we're interleaving. If we set `block_len = 2`, then for + /// + /// ```text + /// A = [x0, x1, y0, y1] + /// B = [x2, x3, y2, y3] + /// ``` + /// + /// we obtain + /// + /// ```text + /// interleave(A, B, block_len) = ([x0, x1, x2, x3], [y0, y1, y2, y3]) + /// ``` + /// + /// We can also think about this as stacking the vectors, dividing them into 2x2 matrices, and + /// transposing those matrices. + /// + /// When `block_len = WIDTH`, this operation is a no-op. + /// + /// # Panics + /// This may panic if `block_len` does not divide `WIDTH`. Since `WIDTH` is specified to be a power of 2, + /// `block_len` must also be a power of 2. It cannot be 0 and it cannot exceed `WIDTH`. + fn interleave(&self, other: Self, block_len: usize) -> (Self, Self); +} + +/// Fix a field `F` a packing width `W` and an extension field `EF` of `F`. +/// +/// By choosing a basis `B`, `EF` can be transformed into an array `[F; D]`. +/// +/// A type should implement PackedFieldExtension if it can be transformed into `[F::Packing; D] ~ [[F; W]; D]` +/// +/// This is interpreted by taking a transpose to get `[[F; D]; W]` which can then be reinterpreted +/// as `[EF; W]` by making use of the chosen basis `B` again. +pub trait PackedFieldExtension< + BaseField: Field, + ExtField: ExtensionField, +>: Algebra + Algebra + BasedVectorSpace +{ + /// Given a slice of extension field `EF` elements of length `W`, + /// convert into the array `[[F; D]; W]` transpose to + /// `[[F; W]; D]` and then pack to get `[PF; D]`. + fn from_ext_slice(ext_slice: &[ExtField]) -> Self; + + /// Given a iterator of packed extension field elements, convert to an iterator of + /// extension field elements. + /// + /// This performs the inverse transformation to `from_ext_slice`. + #[inline] + fn to_ext_iter(iter: impl IntoIterator) -> impl Iterator { + iter.into_iter().flat_map(|x| { + let packed_coeffs = x.as_basis_coefficients_slice(); + (0..BaseField::Packing::WIDTH) + .map(|i| ExtField::from_basis_coefficients_fn(|j| packed_coeffs[j].as_slice()[i])) + .collect::>() // PackedFieldExtension's should reimplement this to avoid this allocation. + }) + } + + /// Similar to `packed_powers`, construct an iterator which returns + /// powers of `base` packed into `PackedFieldExtension` elements. + fn packed_ext_powers(base: ExtField) -> Powers; + + /// Similar to `packed_ext_powers` but only returns `unpacked_len` powers of `base`. + /// + /// Note that the length of the returned iterator will be `unpacked_len / WIDTH` and + /// not `len` as the iterator is over packed extension field elements. If `unpacked_len` + /// is not divisible by `WIDTH`, `unpacked_len` will be rounded up to the next multiple of `WIDTH`. + fn packed_ext_powers_capped(base: ExtField, unpacked_len: usize) -> impl Iterator { + Self::packed_ext_powers(base).take(unpacked_len.div_ceil(BaseField::Packing::WIDTH)) + } +} + +unsafe impl PackedValue for T { + type Value = Self; + + const WIDTH: usize = 1; + + fn from_slice(slice: &[Self::Value]) -> &Self { + &slice[0] + } + + fn from_slice_mut(slice: &mut [Self::Value]) -> &mut Self { + &mut slice[0] + } + + fn from_fn(mut f: Fn) -> Self + where + Fn: FnMut(usize) -> Self::Value, + { + f(0) + } + + fn as_slice(&self) -> &[Self::Value] { + slice::from_ref(self) + } + + fn as_slice_mut(&mut self) -> &mut [Self::Value] { + slice::from_mut(self) + } +} + +unsafe impl PackedField for F { + type Scalar = Self; +} + +unsafe impl PackedFieldPow2 for F { + fn interleave(&self, other: Self, block_len: usize) -> (Self, Self) { + match block_len { + 1 => (*self, other), + _ => panic!("unsupported block length"), + } + } +} + +impl PackedFieldExtension for F::Packing { + fn from_ext_slice(ext_slice: &[F]) -> Self { + *F::Packing::from_slice(ext_slice) + } + + fn packed_ext_powers(base: F) -> Powers { + F::Packing::packed_powers(base) + } +} + +impl Packable for u8 {} + +impl Packable for u16 {} + +impl Packable for u32 {} + +impl Packable for u64 {} + +impl Packable for u128 {} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/tests/coset_tests.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/tests/coset_tests.rs new file mode 100644 index 00000000..37b8a8d1 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/tests/coset_tests.rs @@ -0,0 +1,152 @@ +mod coset { + use p3_baby_bear::BabyBear; + use p3_field::coset::TwoAdicMultiplicativeCoset; + use p3_field::{PrimeCharacteristicRing, TwoAdicField}; + use p3_goldilocks::Goldilocks; + use rand::rngs::SmallRng; + use rand::{Rng, SeedableRng}; + + type BB = BabyBear; + type GL = Goldilocks; + + #[test] + // Checks that a coset of the maximum size allowed by the field (implementation) + // can indeed be constructed + fn test_coset_limit() { + TwoAdicMultiplicativeCoset::::new(BB::ONE, BB::TWO_ADICITY).unwrap(); + } + + #[test] + // Checks that attempting to construct a field larger than allowed by the field + // implementation is disallowed + fn test_coset_too_large() { + assert!(TwoAdicMultiplicativeCoset::::new(BB::ONE, BB::TWO_ADICITY + 1).is_none()); + } + + #[test] + // Checks that attempting to shrink a coset by any divisor of its size is + // allowed, but doing so by the next power of two is not + fn test_shrink_too_much() { + let coset = TwoAdicMultiplicativeCoset::::new(GL::from_u16(42), 5).unwrap(); + + for i in 0..6 { + assert!(coset.shrink_coset(i).is_some()); + } + + assert!(coset.shrink_coset(6).is_none()); + } + + #[test] + // Checks that shrinking by a factor of 2^0 = 1 does nothing + fn test_shrink_nothing() { + let coset = TwoAdicMultiplicativeCoset::::new(BB::ONE, 7).unwrap(); + + let shrunk = coset.shrink_coset(0).unwrap(); + + assert_eq!(shrunk.subgroup_generator(), coset.subgroup_generator()); + assert_eq!(shrunk.shift(), coset.shift()); + } + + #[test] + // Checks that shrinking the whole coset results in the expected new shift + fn test_shrink_shift() { + let mut rng = SmallRng::seed_from_u64(1234); + let shift: BB = rng.random(); + + let coset = TwoAdicMultiplicativeCoset::::new(shift, 4).unwrap(); + let shrunk = coset.exp_power_of_2(2).unwrap(); + + assert_eq!(shrunk.shift(), shift.exp_power_of_2(2)); + } + + #[test] + // Checks that shrinking the coset by a factor of k results in a new coset whose + // i-th element is the original coset's (i * k)-th element + fn test_shrink_contained() { + let mut rng = SmallRng::seed_from_u64(19); + let shift: GL = rng.random(); + + let log_shrinking_factor = 3; + + let mut coset = TwoAdicMultiplicativeCoset::::new(shift, 8).unwrap(); + let shrunk = coset.shrink_coset(log_shrinking_factor).unwrap(); + + for (i, e) in shrunk.iter().enumerate() { + assert_eq!(coset.element(i * (1 << log_shrinking_factor)), e); + } + } + + #[test] + // Checks that generator_exp (access through element() of a coset of shift 1) + // yields the correct power of the generator + fn test_generator_exp() { + let mut coset = TwoAdicMultiplicativeCoset::new(BB::ONE, 10).unwrap(); + + for i in 0..1 << 5 { + assert_eq!( + coset.element(i), + coset.subgroup_generator().exp_u64(i as u64) + ); + } + } + + #[test] + // Checks that the coset iterator yields the expected elements (in the expected + // order) + fn test_coset_iterator() { + let mut rng = SmallRng::seed_from_u64(57); + let shift: BB = rng.random(); + let log_size = 3; + + let mut coset = TwoAdicMultiplicativeCoset::new(shift, log_size).unwrap(); + + assert_eq!(coset.into_iter().count(), 1 << log_size); + for (i, e) in coset.iter().enumerate() { + assert_eq!(coset.element(i), e); + } + } + + #[test] + fn test_element_wrap_around() { + let mut coset = TwoAdicMultiplicativeCoset::new(BB::ONE, 3).unwrap(); + + for i in [1, 2] { + for j in 0..coset.size() { + assert_eq!(coset.element(i * coset.size() + j), coset.element(j)); + } + } + } + + #[test] + // Checks that the element method returns the expected values + fn test_element() { + let mut rng = SmallRng::seed_from_u64(53); + + let shift: GL = rng.random(); + let mut coset = TwoAdicMultiplicativeCoset::new(shift, GL::TWO_ADICITY).unwrap(); + + for _ in 0..100 { + let exp = rng.random::() % (1 << GL::TWO_ADICITY); + let expected = coset.shift() * coset.subgroup_generator().exp_u64(exp); + assert_eq!(coset.element(exp as usize), expected); + } + } + + #[test] + // Checks that the contains method returns true on all elements of the coset + fn test_contains() { + let mut rng = SmallRng::seed_from_u64(1729); + let shift = rng.random(); + + let log_size = 8; + + let coset = TwoAdicMultiplicativeCoset::new(shift, log_size).unwrap(); + + let mut d = BB::ONE; + + for _ in 0..(1 << log_size) { + assert!(coset.contains(coset.shift() * d)); + d *= coset.subgroup_generator(); + } + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/tests/helpers_test.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/tests/helpers_test.rs new file mode 100644 index 00000000..fe3ff8ca --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-field/tests/helpers_test.rs @@ -0,0 +1,211 @@ +mod helpers { + use p3_baby_bear::BabyBear; + use p3_field::{ + PrimeCharacteristicRing, add_scaled_slice_in_place, dot_product, field_to_array, + par_add_scaled_slice_in_place, reduce_32, split_32, + }; + + #[test] + fn test_add_scaled_slice_in_place() { + // x = [1, 2], y = [10, 20], scale by 3 + let x1 = BabyBear::ONE; + let x2 = BabyBear::TWO; + let mut x = vec![x1, x2]; + let mut par_x = x.clone(); + + let y1 = BabyBear::from_u8(10); + let y2 = BabyBear::from_u8(20); + let y = vec![y1, y2]; + let s = BabyBear::from_u8(3); + + add_scaled_slice_in_place(&mut x, &y, s); + par_add_scaled_slice_in_place(&mut par_x, &y, s); + + // x = [x1 + s * y1, x2 + s * y2] + let expected = vec![x1 + s * y1, x2 + s * y2]; + + assert_eq!(x, expected); + } + + #[test] + fn test_add_scaled_slice_in_place_zero_scale() { + let original = vec![BabyBear::from_u8(4), BabyBear::from_u8(5)]; + let mut x = original.clone(); + let mut par_x = original.clone(); + let y = vec![BabyBear::from_u8(6), BabyBear::from_u8(7)]; + let s = BabyBear::ZERO; + + add_scaled_slice_in_place(&mut x, &y, s); + par_add_scaled_slice_in_place(&mut par_x, &y, s); + + assert_eq!(x, original); + assert_eq!(par_x, original); + } + + #[test] + fn test_field_to_array() { + // Convert value 9 to array of size 4 + let x = BabyBear::from_u8(9); + let arr = field_to_array::(x); + + // Should yield [9, 0, 0, 0] + assert_eq!(arr, [x, BabyBear::ZERO, BabyBear::ZERO, BabyBear::ZERO]); + } + + #[test] + fn test_field_to_array_single() { + let x = BabyBear::from_u8(99); + let arr = field_to_array::(x); + assert_eq!(arr, [x]); + } + + #[test] + fn test_reduce_32() { + // Input: vals = [1, 2, 3] + let vals = [BabyBear::ONE, BabyBear::TWO, BabyBear::from_u32(3)]; + + // po2 = 2^32 mod BabyBear = 1048575 + let po2 = BabyBear::from_u64(1u64 << 32); // 2^32 mod field + + // Manual reduction process (reverse order): + // Step 1: result = 0 + // Step 2: result = result * po2 + 3 + // Step 3: result = result * po2 + 2 + // Step 4: result = result * po2 + 1 + + let step1 = BabyBear::ZERO; + let step2 = step1 * po2 + vals[2]; + let step3 = step2 * po2 + vals[1]; + let expected = step3 * po2 + vals[0]; + + let result = reduce_32::(&vals); + assert_eq!(result, expected); + } + + #[test] + fn test_reduce_32_large_vector_high_entropy() { + // Input: vals = [1, 2, 3, ..., 10] + let vals: Vec = (1..=10).map(BabyBear::from_u32).collect(); + + let po2 = BabyBear::from_u64(1u64 << 32); // base = 2^32 + + // Manual computation step-by-step: + let step10 = BabyBear::from_u32(10); + let step9 = step10 * po2 + BabyBear::from_u32(9); + let step8 = step9 * po2 + BabyBear::from_u32(8); + let step7 = step8 * po2 + BabyBear::from_u32(7); + let step6 = step7 * po2 + BabyBear::from_u32(6); + let step5 = step6 * po2 + BabyBear::from_u32(5); + let step4 = step5 * po2 + BabyBear::from_u32(4); + let step3 = step4 * po2 + BabyBear::from_u32(3); + let step2 = step3 * po2 + BabyBear::TWO; + let expected = step2 * po2 + BabyBear::ONE; + + let result = reduce_32::(&vals); + assert_eq!(result, expected); + } + + #[test] + fn test_reduce_32_empty() { + let vals: [BabyBear; 0] = []; + let result = reduce_32::(&vals); + assert_eq!(result, BabyBear::ZERO); + } + + #[test] + fn test_split_32_round_trip() { + // Choose any field element as base input (already reduced) + let val = BabyBear::from_u32(1172168165); + + // Split it into base-2^64 "digits" + let parts = split_32::(val, 2); + + // Recombine it using reduce_32 + let recomposed = reduce_32::(&parts); + + // It should match the original value + assert_eq!(recomposed, val); + } + + #[test] + fn test_split_32_zero() { + let val = BabyBear::ZERO; + let parts = split_32::(val, 3); + + assert_eq!(parts, vec![BabyBear::ZERO; 3]); + } + + #[test] + fn test_dot_product() { + let a1 = BabyBear::TWO; + let a2 = BabyBear::from_u8(4); + let a3 = BabyBear::from_u8(6); + let a = [a1, a2, a3]; + + let b1 = BabyBear::from_u8(3); + let b2 = BabyBear::from_u8(5); + let b3 = BabyBear::from_u8(7); + let b = [b1, b2, b3]; + + // 2*3 + 4*5 + 6*7 + let expected = a1 * b1 + a2 * b2 + a3 * b3; + + let result = dot_product::(a.iter().copied(), b.iter().copied()); + assert_eq!(result, expected); + } + + #[test] + fn test_dot_product_empty() { + let a: Vec = vec![]; + let b: Vec = vec![]; + let result = dot_product::(a.into_iter(), b.into_iter()); + assert_eq!(result, BabyBear::ZERO); + } + + #[test] + fn test_dot_product_mismatched_lengths() { + let a1 = BabyBear::TWO; + let a2 = BabyBear::from_u8(4); + let a = vec![a1, a2]; + + let b1 = BabyBear::from_u8(3); + let b2 = BabyBear::from_u8(5); + let b3 = BabyBear::from_u8(7); + let b = vec![b1, b2, b3]; + + // Only first two elements will be multiplied + let expected = a1 * b1 + a2 * b2; + + let result = dot_product::(a.into_iter(), b.into_iter()); + assert_eq!(result, expected); + } + + #[test] + fn test_field_to_array_complex() { + use p3_baby_bear::BabyBear; + use p3_field::field_to_array; + + // Case 1: Non-zero element, D = 5 + let x = BabyBear::from_u32(123); + let arr = field_to_array::(x); + + // Should produce: [123, 0, 0, 0, 0] + assert_eq!( + arr, + [ + BabyBear::from_u32(123), + BabyBear::ZERO, + BabyBear::ZERO, + BabyBear::ZERO, + BabyBear::ZERO + ] + ); + + // Case 2: Zero input value + let x = BabyBear::ZERO; + let arr = field_to_array::(x); + + // Should be all zeros: [0, 0, 0] + assert_eq!(arr, [BabyBear::ZERO; 3]); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/.cargo-ok b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/.cargo-ok new file mode 100644 index 00000000..5f8b7958 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/.cargo_vcs_info.json b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/.cargo_vcs_info.json new file mode 100644 index 00000000..009c21ea --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/.cargo_vcs_info.json @@ -0,0 +1,7 @@ +{ + "git": { + "sha1": "3148b7148a19bb56dedb55291ae9b06223904e88", + "dirty": true + }, + "path_in_vcs": "matrix" +} \ No newline at end of file diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.lock b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.lock new file mode 100644 index 00000000..e5ee4a9f --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.lock @@ -0,0 +1,717 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "criterion" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[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.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "p3-field" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc13a73509fe09c67b339951ca8d4cc6e61c9bf08c130dbc90dda52452918cc2" +dependencies = [ + "itertools 0.14.0", + "num-bigint", + "p3-maybe-rayon", + "p3-util", + "paste", + "rand", + "serde", + "tracing", +] + +[[package]] +name = "p3-matrix" +version = "0.3.0" +dependencies = [ + "criterion", + "itertools 0.14.0", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand", + "serde", + "tracing", + "transpose", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f765046b763d046728b3246b690f81dfa7ccd7523b7a1582c74f616fbce6a0" + +[[package]] +name = "p3-util" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfee67245d9ce78a15176728da2280032f0a84b5819a39a953e7ec03cfd9bd7" +dependencies = [ + "serde", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.toml b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.toml new file mode 100644 index 00000000..48bb2d8f --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.toml @@ -0,0 +1,81 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +name = "p3-matrix" +version = "0.3.0" +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Plonky3 is a toolkit for implementing polynomial IOPs (PIOPs), such as PLONK and STARKs." +homepage = "https://github.com/0xPolygonZero/Plonky3" +readme = false +keywords = [ + "cryptography", + "SNARK", + "PLONK", + "FRI", + "plonky3", +] +categories = ["cryptography::cryptocurrencies"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/0xPolygonZero/Plonky3" +resolver = "2" + +[lib] +name = "p3_matrix" +path = "src/lib.rs" + +[[bench]] +name = "columnwise_dot_product" +path = "benches/columnwise_dot_product.rs" +harness = false + +[[bench]] +name = "transpose_benchmark" +path = "benches/transpose_benchmark.rs" +harness = false + +[dependencies.itertools] +version = "0.14.0" +features = ["use_alloc"] +default-features = false + +[dependencies.p3-field] +version = "0.3.0" + +[dependencies.p3-maybe-rayon] +version = "0.3.0" + +[dependencies.p3-util] +version = "0.3.0" + +[dependencies.rand] +version = "0.9.0" +features = ["small_rng"] +default-features = false + +[dependencies.serde] +version = "1.0" +features = ["derive"] +default-features = false +["attributes"] +default-features = false + +[dependencies.transpose] +version = "0.2.3" + +[dev-dependencies.criterion] +version = "0.6" diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.toml.orig b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.toml.orig new file mode 100644 index 00000000..c9ec8f0e --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/Cargo.toml.orig @@ -0,0 +1,38 @@ +[package] +name = "p3-matrix" +# TODO: Replace this generic plonky3 description with one specific to this crate... +description.workspace = true +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +keywords.workspace = true +categories.workspace = true + +[dependencies] +p3-field.workspace = true +p3-maybe-rayon.workspace = true +p3-util.workspace = true + +itertools.workspace = true +rand.workspace = true +serde = { workspace = true, features = ["derive"] } +tracing.workspace = true +transpose.workspace = true + +[dev-dependencies] +p3-baby-bear = { path = "../baby-bear" } +p3-mersenne-31 = { path = "../mersenne-31" } + +criterion.workspace = true + +[[bench]] +name = "transpose_benchmark" +path = "benches/transpose_benchmark.rs" +harness = false + +[[bench]] +name = "columnwise_dot_product" +path = "benches/columnwise_dot_product.rs" +harness = false diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/benches/columnwise_dot_product.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/benches/columnwise_dot_product.rs new file mode 100644 index 00000000..7b236192 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/benches/columnwise_dot_product.rs @@ -0,0 +1,33 @@ +use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; +use p3_baby_bear::BabyBear; +use p3_field::extension::BinomialExtensionField; +use p3_matrix::Matrix; +use p3_matrix::dense::RowMajorMatrix; +use rand::SeedableRng; +use rand::rngs::SmallRng; + +fn columnwise_dot_product(c: &mut Criterion) { + let mut rng = SmallRng::seed_from_u64(0); + + type F = BabyBear; + type EF = BinomialExtensionField; + let log_rows = 16; + + c.benchmark_group("babybear") + .sample_size(10) + .bench_function("columnwise_dot_product", |b| { + b.iter_batched( + || { + ( + RowMajorMatrix::::rand_nonzero(&mut rng, 1 << log_rows, 1 << 12), + RowMajorMatrix::::rand_nonzero(&mut rng, 1 << log_rows, 1).values, + ) + }, + |(m, v)| m.columnwise_dot_product(&v), + BatchSize::PerIteration, + ); + }); +} + +criterion_group!(benches, columnwise_dot_product); +criterion_main!(benches); diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/benches/transpose_benchmark.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/benches/transpose_benchmark.rs new file mode 100644 index 00000000..456eee78 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/benches/transpose_benchmark.rs @@ -0,0 +1,42 @@ +use criterion::{BenchmarkGroup, Criterion, Throughput, criterion_group, criterion_main}; +use p3_matrix::dense::RowMajorMatrix; +use rand::SeedableRng; +use rand::rngs::SmallRng; + +fn transpose_benchmark(c: &mut Criterion) { + const SMALL_DIMS: [(usize, usize); 4] = [(4, 4), (8, 8), (10, 10), (12, 12)]; + const LARGE_DIMS: [(usize, usize); 4] = [(20, 8), (21, 8), (22, 8), (23, 8)]; + + let inner = |g: &mut BenchmarkGroup<_>, dims: &[(usize, usize)]| { + let mut rng = SmallRng::seed_from_u64(1); + for (lg_nrows, lg_ncols) in dims { + let nrows = 1 << lg_nrows; + let ncols = 1 << lg_ncols; + let mut matrix1 = RowMajorMatrix::::rand(&mut rng, nrows, ncols); + let mut matrix2 = RowMajorMatrix::default(nrows, ncols); + + let name = format!("2^{lg_nrows} x 2^{lg_ncols}"); + g.throughput(Throughput::Bytes( + (nrows * ncols * core::mem::size_of::()) as u64, + )); + g.bench_function(&name, |b| b.iter(|| matrix1.transpose_into(&mut matrix2))); + + if nrows != ncols { + let matrix2 = RowMajorMatrix::rand(&mut rng, ncols, nrows); + let name = format!("2^{lg_ncols} x 2^{lg_nrows}"); + g.throughput(Throughput::Bytes( + (nrows * ncols * core::mem::size_of::()) as u64, + )); + g.bench_function(&name, |b| b.iter(|| matrix2.transpose_into(&mut matrix1))); + } + } + }; + + let mut g = c.benchmark_group("transpose"); + inner(&mut g, &SMALL_DIMS); + g.sample_size(10); + inner(&mut g, &LARGE_DIMS); +} + +criterion_group!(benches, transpose_benchmark); +criterion_main!(benches); diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/bitrev.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/bitrev.rs new file mode 100644 index 00000000..8ba37f87 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/bitrev.rs @@ -0,0 +1,169 @@ +use p3_util::{log2_strict_usize, reverse_bits_len}; + +use crate::Matrix; +use crate::dense::{DenseMatrix, DenseStorage, RowMajorMatrix}; +use crate::row_index_mapped::{RowIndexMap, RowIndexMappedView}; +use crate::util::reverse_matrix_index_bits; + +/// A trait for matrices that support *bit-reversed row reordering*. +/// +/// Implementors of this trait can switch between row-major order and bit-reversed +/// row order (i.e., reversing the binary representation of each row index). +/// +/// This trait allows interoperability between regular matrices and views +/// that access their rows in a bit-reversed order. +pub trait BitReversibleMatrix: Matrix { + /// The type returned when this matrix is viewed in bit-reversed order. + type BitRev: BitReversibleMatrix; + + /// Return a version of the matrix with its row order reversed by bit index. + fn bit_reverse_rows(self) -> Self::BitRev; +} + +/// A row index permutation that reorders rows according to bit-reversed index. +/// +/// Used internally to implement `BitReversedMatrixView`. +#[derive(Debug)] +pub struct BitReversalPerm { + /// The logarithm (base 2) of the matrix height. For height `h`, this is `log2(h)`. + /// + /// This must be exact, so the height must be a power of two. + log_height: usize, +} + +impl BitReversalPerm { + /// Create a new bit-reversal view over the given matrix. + /// + /// # Panics + /// Panics if the height of the matrix is not a power of two. + /// + /// # Arguments + /// - `inner`: The matrix to wrap in a bit-reversed row view. + /// + /// # Returns + /// A `BitReversedMatrixView` that wraps the input with row reordering. + pub fn new_view>( + inner: Inner, + ) -> BitReversedMatrixView { + RowIndexMappedView { + index_map: Self { + log_height: log2_strict_usize(inner.height()), + }, + inner, + } + } +} + +impl RowIndexMap for BitReversalPerm { + fn height(&self) -> usize { + 1 << self.log_height + } + + fn map_row_index(&self, r: usize) -> usize { + reverse_bits_len(r, self.log_height) + } + + // This might not be more efficient than the lazy generic impl + // if we have a nested view. + fn to_row_major_matrix>( + &self, + inner: Inner, + ) -> RowMajorMatrix { + let mut inner = inner.to_row_major_matrix(); + reverse_matrix_index_bits(&mut inner); + inner + } +} + +/// A matrix view that reorders its rows using bit-reversal. +/// +/// This type is produced by applying `BitReversibleMatrix::bit_reverse_rows()` +/// to a `DenseMatrix`. +pub type BitReversedMatrixView = RowIndexMappedView; + +impl> BitReversibleMatrix + for BitReversedMatrixView> +{ + type BitRev = DenseMatrix; + + fn bit_reverse_rows(self) -> Self::BitRev { + self.inner + } +} + +impl> BitReversibleMatrix for DenseMatrix { + type BitRev = BitReversedMatrixView; + + fn bit_reverse_rows(self) -> Self::BitRev { + BitReversalPerm::new_view(self) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + use alloc::vec::Vec; + + use super::*; + + #[test] + fn test_bit_reversal_perm_map_index() { + let perm = BitReversalPerm { log_height: 3 }; // height = 8 + assert_eq!(perm.map_row_index(0), 0); // 000 -> 000 + assert_eq!(perm.map_row_index(1), 4); // 001 -> 100 + assert_eq!(perm.map_row_index(2), 2); // 010 -> 010 + assert_eq!(perm.map_row_index(3), 6); // 011 -> 110 + assert_eq!(perm.map_row_index(4), 1); // 100 -> 001 + assert_eq!(perm.map_row_index(5), 5); // 101 -> 101 + assert_eq!(perm.map_row_index(6), 3); // 110 -> 011 + assert_eq!(perm.map_row_index(7), 7); // 111 -> 111 + } + + #[test] + fn test_bit_reversal_perm_height() { + let perm = BitReversalPerm { log_height: 3 }; + assert_eq!(perm.height(), 8); // 2^3 + } + + #[test] + fn test_new_view_reverses_indices_correctly() { + // Matrix with height = 8 (2^3), width = 1: [0,1,2,3,4,5,6,7] + let matrix = RowMajorMatrix::new((0u32..8).collect::>(), 1); + let bitrev = BitReversalPerm::new_view(matrix); + + // Should map row indices via bit reversal + let expected = [0, 4, 2, 6, 1, 5, 3, 7]; + for (i, &expected_row_idx) in expected.iter().enumerate() { + let row: Vec<_> = bitrev.row(i).unwrap().into_iter().collect(); + assert_eq!(row, vec![expected_row_idx]); + } + } + + #[test] + fn test_to_row_major_matrix_applies_reverse_matrix_index_bits() { + let matrix = RowMajorMatrix::new((0u32..8).collect::>(), 1); + let perm = BitReversalPerm { log_height: 3 }; + + let reordered = perm.to_row_major_matrix(matrix); + let expected_values = vec![0, 4, 2, 6, 1, 5, 3, 7]; // bit-reversed row order + assert_eq!(reordered.values, expected_values); + } + + #[test] + fn test_bit_reversible_matrix_trait_forward_reverse() { + let original = RowMajorMatrix::new((0u32..8).collect::>(), 1); + let reversed_view = original.clone().bit_reverse_rows(); // -> BitReversedMatrixView + let back_to_dense = reversed_view.bit_reverse_rows(); // -> back to DenseMatrix + + assert_eq!(original.values, back_to_dense.values); + assert_eq!(original.width(), back_to_dense.width()); + } + + #[test] + #[should_panic] + fn test_new_view_panics_non_pow2_height() { + // This matrix has height = 3 (not a power of two) + let matrix = RowMajorMatrix::new(vec![1, 2, 3], 1); + let _ = BitReversalPerm::new_view(matrix); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/dense.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/dense.rs new file mode 100644 index 00000000..e01f6492 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/dense.rs @@ -0,0 +1,1304 @@ +use alloc::borrow::Cow; +use alloc::vec; +use alloc::vec::Vec; +use core::borrow::{Borrow, BorrowMut}; +use core::iter; +use core::marker::PhantomData; +use core::ops::Deref; + +use p3_field::{ + ExtensionField, Field, PackedValue, par_scale_slice_in_place, scale_slice_in_place_single_core, +}; +use p3_maybe_rayon::prelude::*; +use rand::Rng; +use rand::distr::{Distribution, StandardUniform}; +use serde::{Deserialize, Serialize}; + +use crate::Matrix; + +/// A dense matrix in row-major format, with customizable backing storage. +/// +/// The data is stored as a flat buffer, where rows are laid out consecutively. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct DenseMatrix> { + /// Flat buffer of matrix values in row-major order. + pub values: V, + /// Number of columns in the matrix. + /// + /// The number of rows is implicitly determined as `values.len() / width`. + pub width: usize, + /// Marker for the element type `T`, unused directly. + /// + /// Required to retain type information when `V` does not own or contain `T`. + _phantom: PhantomData, +} + +pub type RowMajorMatrix = DenseMatrix; +pub type RowMajorMatrixView<'a, T> = DenseMatrix; +pub type RowMajorMatrixViewMut<'a, T> = DenseMatrix; +pub type RowMajorMatrixCow<'a, T> = DenseMatrix>; + +pub trait DenseStorage: Borrow<[T]> + Send + Sync { + fn to_vec(self) -> Vec; +} + +// Cow doesn't impl IntoOwned so we can't blanket it +impl DenseStorage for Vec { + fn to_vec(self) -> Self { + self + } +} + +impl DenseStorage for &[T] { + fn to_vec(self) -> Vec { + <[T]>::to_vec(self) + } +} + +impl DenseStorage for &mut [T] { + fn to_vec(self) -> Vec { + <[T]>::to_vec(self) + } +} + +impl DenseStorage for Cow<'_, [T]> { + fn to_vec(self) -> Vec { + self.into_owned() + } +} + +impl DenseMatrix { + /// Create a new dense matrix of the given dimensions, backed by a `Vec`, and filled with + /// default values. + #[must_use] + pub fn default(width: usize, height: usize) -> Self { + Self::new(vec![T::default(); width * height], width) + } +} + +impl> DenseMatrix { + /// Create a new dense matrix of the given dimensions, backed by the given storage. + /// + /// Note that it is undefined behavior to create a matrix such that + /// `values.len() % width != 0`. + #[must_use] + pub fn new(values: S, width: usize) -> Self { + debug_assert!(width == 0 || values.borrow().len() % width == 0); + Self { + values, + width, + _phantom: PhantomData, + } + } + + /// Create a new RowMajorMatrix containing a single row. + #[must_use] + pub fn new_row(values: S) -> Self { + let width = values.borrow().len(); + Self::new(values, width) + } + + /// Create a new RowMajorMatrix containing a single column. + #[must_use] + pub fn new_col(values: S) -> Self { + Self::new(values, 1) + } + + /// Get a view of the matrix, i.e. a reference to the underlying data. + pub fn as_view(&self) -> RowMajorMatrixView<'_, T> { + RowMajorMatrixView::new(self.values.borrow(), self.width) + } + + /// Get a mutable view of the matrix, i.e. a mutable reference to the underlying data. + pub fn as_view_mut(&mut self) -> RowMajorMatrixViewMut<'_, T> + where + S: BorrowMut<[T]>, + { + RowMajorMatrixViewMut::new(self.values.borrow_mut(), self.width) + } + + /// Copy the values from the given matrix into this matrix. + pub fn copy_from(&mut self, source: &DenseMatrix) + where + T: Copy, + S: BorrowMut<[T]>, + S2: DenseStorage, + { + assert_eq!(self.dimensions(), source.dimensions()); + // Equivalent to: + // self.values.borrow_mut().copy_from_slice(source.values.borrow()); + self.par_rows_mut() + .zip(source.par_row_slices()) + .for_each(|(dst, src)| { + dst.copy_from_slice(src); + }); + } + + /// Flatten an extension field matrix to a base field matrix. + pub fn flatten_to_base(self) -> RowMajorMatrix + where + T: ExtensionField, + { + let width = self.width * T::DIMENSION; + let values = T::flatten_to_base(self.values.to_vec()); + RowMajorMatrix::new(values, width) + } + + /// Get an iterator over the rows of the matrix. + pub fn row_slices(&self) -> impl Iterator { + self.values.borrow().chunks_exact(self.width) + } + + /// Get a parallel iterator over the rows of the matrix. + pub fn par_row_slices(&self) -> impl IndexedParallelIterator + where + T: Sync, + { + self.values.borrow().par_chunks_exact(self.width) + } + + /// Returns a slice of the given row. + /// + /// # Panics + /// Panics if `r` larger than self.height(). + pub fn row_mut(&mut self, r: usize) -> &mut [T] + where + S: BorrowMut<[T]>, + { + &mut self.values.borrow_mut()[r * self.width..(r + 1) * self.width] + } + + /// Get a mutable iterator over the rows of the matrix. + pub fn rows_mut(&mut self) -> impl Iterator + where + S: BorrowMut<[T]>, + { + self.values.borrow_mut().chunks_exact_mut(self.width) + } + + /// Get a mutable parallel iterator over the rows of the matrix. + pub fn par_rows_mut<'a>(&'a mut self) -> impl IndexedParallelIterator + where + T: 'a + Send, + S: BorrowMut<[T]>, + { + self.values.borrow_mut().par_chunks_exact_mut(self.width) + } + + /// Get a mutable iterator over the rows of the matrix which packs the rows into packed values. + /// + /// If `P::WIDTH` does not divide `self.width`, the remainder of the row will be returned as a + /// base slice. + pub fn horizontally_packed_row_mut

(&mut self, r: usize) -> (&mut [P], &mut [T]) + where + P: PackedValue, + S: BorrowMut<[T]>, + { + P::pack_slice_with_suffix_mut(self.row_mut(r)) + } + + /// Scale the given row by the given value. + /// + /// # Panics + /// Panics if `r` larger than `self.height()`. + pub fn scale_row(&mut self, r: usize, scale: T) + where + T: Field, + S: BorrowMut<[T]>, + { + scale_slice_in_place_single_core(self.row_mut(r), scale); + } + + /// Scale the given row by the given value. + /// + /// # Performance + /// This function is parallelized, which may introduce some overhead compared to + /// [`Self::scale_row`] when the width is small. + /// + /// # Panics + /// Panics if `r` larger than `self.height()`. + pub fn par_scale_row(&mut self, r: usize, scale: T) + where + T: Field, + S: BorrowMut<[T]>, + { + par_scale_slice_in_place(self.row_mut(r), scale); + } + + /// Scale the entire matrix by the given value. + pub fn scale(&mut self, scale: T) + where + T: Field, + S: BorrowMut<[T]>, + { + par_scale_slice_in_place(self.values.borrow_mut(), scale); + } + + /// Split the matrix into two matrix views, one with the first `r` rows and one with the remaining rows. + /// + /// # Panics + /// Panics if `r` larger than `self.height()`. + pub fn split_rows(&self, r: usize) -> (RowMajorMatrixView, RowMajorMatrixView) { + let (lo, hi) = self.values.borrow().split_at(r * self.width); + ( + DenseMatrix::new(lo, self.width), + DenseMatrix::new(hi, self.width), + ) + } + + /// Split the matrix into two mutable matrix views, one with the first `r` rows and one with the remaining rows. + /// + /// # Panics + /// Panics if `r` larger than `self.height()`. + pub fn split_rows_mut( + &mut self, + r: usize, + ) -> (RowMajorMatrixViewMut, RowMajorMatrixViewMut) + where + S: BorrowMut<[T]>, + { + let (lo, hi) = self.values.borrow_mut().split_at_mut(r * self.width); + ( + DenseMatrix::new(lo, self.width), + DenseMatrix::new(hi, self.width), + ) + } + + /// Get an iterator over the rows of the matrix which takes `chunk_rows` rows at a time. + /// + /// If `chunk_rows` does not divide the height of the matrix, the last chunk will be smaller. + pub fn par_row_chunks( + &self, + chunk_rows: usize, + ) -> impl IndexedParallelIterator> + where + T: Send, + { + self.values + .borrow() + .par_chunks(self.width * chunk_rows) + .map(|slice| RowMajorMatrixView::new(slice, self.width)) + } + + /// Get a parallel iterator over the rows of the matrix which takes `chunk_rows` rows at a time. + /// + /// If `chunk_rows` does not divide the height of the matrix, the last chunk will be smaller. + pub fn par_row_chunks_exact( + &self, + chunk_rows: usize, + ) -> impl IndexedParallelIterator> + where + T: Send, + { + self.values + .borrow() + .par_chunks_exact(self.width * chunk_rows) + .map(|slice| RowMajorMatrixView::new(slice, self.width)) + } + + /// Get a mutable iterator over the rows of the matrix which takes `chunk_rows` rows at a time. + /// + /// If `chunk_rows` does not divide the height of the matrix, the last chunk will be smaller. + pub fn par_row_chunks_mut( + &mut self, + chunk_rows: usize, + ) -> impl IndexedParallelIterator> + where + T: Send, + S: BorrowMut<[T]>, + { + self.values + .borrow_mut() + .par_chunks_mut(self.width * chunk_rows) + .map(|slice| RowMajorMatrixViewMut::new(slice, self.width)) + } + + /// Get a mutable iterator over the rows of the matrix which takes `chunk_rows` rows at a time. + /// + /// If `chunk_rows` does not divide the height of the matrix, the last up to `chunk_rows - 1` rows + /// of the matrix will be omitted. + pub fn row_chunks_exact_mut( + &mut self, + chunk_rows: usize, + ) -> impl Iterator> + where + T: Send, + S: BorrowMut<[T]>, + { + self.values + .borrow_mut() + .chunks_exact_mut(self.width * chunk_rows) + .map(|slice| RowMajorMatrixViewMut::new(slice, self.width)) + } + + /// Get a parallel mutable iterator over the rows of the matrix which takes `chunk_rows` rows at a time. + /// + /// If `chunk_rows` does not divide the height of the matrix, the last up to `chunk_rows - 1` rows + /// of the matrix will be omitted. + pub fn par_row_chunks_exact_mut( + &mut self, + chunk_rows: usize, + ) -> impl IndexedParallelIterator> + where + T: Send, + S: BorrowMut<[T]>, + { + self.values + .borrow_mut() + .par_chunks_exact_mut(self.width * chunk_rows) + .map(|slice| RowMajorMatrixViewMut::new(slice, self.width)) + } + + /// Get a pair of mutable slices of the given rows. + /// + /// # Panics + /// Panics if `row_1` or `row_2` are out of bounds or if `row_1 >= row_2`. + pub fn row_pair_mut(&mut self, row_1: usize, row_2: usize) -> (&mut [T], &mut [T]) + where + S: BorrowMut<[T]>, + { + debug_assert_ne!(row_1, row_2); + let start_1 = row_1 * self.width; + let start_2 = row_2 * self.width; + let (lo, hi) = self.values.borrow_mut().split_at_mut(start_2); + (&mut lo[start_1..][..self.width], &mut hi[..self.width]) + } + + /// Get a pair of mutable slices of the given rows, both packed into packed field elements. + /// + /// If `P:WIDTH` does not divide `self.width`, the remainder of the row will be returned as a base slice. + /// + /// # Panics + /// Panics if `row_1` or `row_2` are out of bounds or if `row_1 >= row_2`. + #[allow(clippy::type_complexity)] + pub fn packed_row_pair_mut

( + &mut self, + row_1: usize, + row_2: usize, + ) -> ((&mut [P], &mut [T]), (&mut [P], &mut [T])) + where + S: BorrowMut<[T]>, + P: PackedValue, + { + let (slice_1, slice_2) = self.row_pair_mut(row_1, row_2); + ( + P::pack_slice_with_suffix_mut(slice_1), + P::pack_slice_with_suffix_mut(slice_2), + ) + } + + /// Append zeros to the "end" of the given matrix, except that the matrix is in bit-reversed order, + /// so in actuality we're interleaving zero rows. + pub fn bit_reversed_zero_pad(self, added_bits: usize) -> RowMajorMatrix + where + T: Field, + { + if added_bits == 0 { + return self.to_row_major_matrix(); + } + + // This is equivalent to: + // reverse_matrix_index_bits(mat); + // mat + // .values + // .resize(mat.values.len() << added_bits, F::ZERO); + // reverse_matrix_index_bits(mat); + // But rather than implement it with bit reversals, we directly construct the resulting matrix, + // whose rows are zero except for rows whose low `added_bits` bits are zero. + + let w = self.width; + let mut padded = + RowMajorMatrix::new(T::zero_vec(self.values.borrow().len() << added_bits), w); + padded + .par_row_chunks_exact_mut(1 << added_bits) + .zip(self.par_row_slices()) + .for_each(|(mut ch, r)| ch.row_mut(0).copy_from_slice(r)); + + padded + } +} + +impl> Matrix for DenseMatrix { + #[inline] + fn width(&self) -> usize { + self.width + } + + #[inline] + fn height(&self) -> usize { + if self.width == 0 { + 0 + } else { + self.values.borrow().len() / self.width + } + } + + #[inline] + unsafe fn get_unchecked(&self, r: usize, c: usize) -> T { + unsafe { + // Safety: The caller must ensure that r < self.height() and c < self.width(). + self.values + .borrow() + .get_unchecked(r * self.width + c) + .clone() + } + } + + #[inline] + unsafe fn row_subseq_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height() and start <= end <= self.width(). + self.values + .borrow() + .get_unchecked(r * self.width + start..r * self.width + end) + .iter() + .cloned() + } + } + + #[inline] + unsafe fn row_subslice_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl Deref { + unsafe { + // Safety: The caller must ensure that r < self.height() + self.values + .borrow() + .get_unchecked(r * self.width + start..r * self.width + end) + } + } + + fn to_row_major_matrix(self) -> RowMajorMatrix + where + Self: Sized, + T: Clone, + { + RowMajorMatrix::new(self.values.to_vec(), self.width) + } + + #[inline] + fn horizontally_packed_row<'a, P>( + &'a self, + r: usize, + ) -> ( + impl Iterator + Send + Sync, + impl Iterator + Send + Sync, + ) + where + P: PackedValue, + T: Clone + 'a, + { + let buf = &self.values.borrow()[r * self.width..(r + 1) * self.width]; + let (packed, sfx) = P::pack_slice_with_suffix(buf); + (packed.iter().copied(), sfx.iter().cloned()) + } + + #[inline] + fn padded_horizontally_packed_row<'a, P>( + &'a self, + r: usize, + ) -> impl Iterator + Send + Sync + where + P: PackedValue, + T: Clone + Default + 'a, + { + let buf = &self.values.borrow()[r * self.width..(r + 1) * self.width]; + let (packed, sfx) = P::pack_slice_with_suffix(buf); + packed.iter().copied().chain(iter::once(P::from_fn(|i| { + sfx.get(i).cloned().unwrap_or_default() + }))) + } +} + +impl DenseMatrix { + pub fn as_cow<'a>(self) -> RowMajorMatrixCow<'a, T> { + RowMajorMatrixCow::new(Cow::Owned(self.values), self.width) + } + + pub fn rand(rng: &mut R, rows: usize, cols: usize) -> Self + where + StandardUniform: Distribution, + { + let values = rng.sample_iter(StandardUniform).take(rows * cols).collect(); + Self::new(values, cols) + } + + pub fn rand_nonzero(rng: &mut R, rows: usize, cols: usize) -> Self + where + T: Field, + StandardUniform: Distribution, + { + let values = rng + .sample_iter(StandardUniform) + .filter(|x| !x.is_zero()) + .take(rows * cols) + .collect(); + Self::new(values, cols) + } + + pub fn pad_to_height(&mut self, new_height: usize, fill: T) { + assert!(new_height >= self.height()); + self.values.resize(self.width * new_height, fill); + } +} + +impl> DenseMatrix { + /// Return the transpose of this matrix. + pub fn transpose(&self) -> RowMajorMatrix { + let nelts = self.height() * self.width(); + let mut values = vec![T::default(); nelts]; + transpose::transpose( + self.values.borrow(), + &mut values, + self.width(), + self.height(), + ); + RowMajorMatrix::new(values, self.height()) + } + + /// Transpose the matrix returning the result in `other` without intermediate allocation. + pub fn transpose_into + BorrowMut<[T]>>( + &self, + other: &mut DenseMatrix, + ) { + assert_eq!(self.height(), other.width()); + assert_eq!(other.height(), self.width()); + transpose::transpose( + self.values.borrow(), + other.values.borrow_mut(), + self.width(), + self.height(), + ); + } +} + +impl<'a, T: Clone + Default + Send + Sync> RowMajorMatrixView<'a, T> { + pub fn as_cow(self) -> RowMajorMatrixCow<'a, T> { + RowMajorMatrixCow::new(Cow::Borrowed(self.values), self.width) + } +} + +#[cfg(test)] +mod tests { + use p3_baby_bear::BabyBear; + use p3_field::FieldArray; + + use super::*; + + #[test] + fn test_new() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + assert_eq!(matrix.width, 2); + assert_eq!(matrix.height(), 3); + assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6]); + } + + #[test] + fn test_new_row() { + let matrix = RowMajorMatrix::new_row(vec![1, 2, 3]); + assert_eq!(matrix.width, 3); + assert_eq!(matrix.height(), 1); + } + + #[test] + fn test_new_col() { + let matrix = RowMajorMatrix::new_col(vec![1, 2, 3]); + assert_eq!(matrix.width, 1); + assert_eq!(matrix.height(), 3); + } + + #[test] + fn test_height_with_zero_width() { + let matrix: DenseMatrix = RowMajorMatrix::new(vec![], 0); + assert_eq!(matrix.height(), 0); + } + + #[test] + fn test_get_methods() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); // Height = 3, Width = 2 + assert_eq!(matrix.get(0, 0), Some(1)); + assert_eq!(matrix.get(1, 1), Some(4)); + assert_eq!(matrix.get(2, 0), Some(5)); + unsafe { + assert_eq!(matrix.get_unchecked(0, 1), 2); + assert_eq!(matrix.get_unchecked(1, 0), 3); + assert_eq!(matrix.get_unchecked(2, 1), 6); + } + assert_eq!(matrix.get(3, 0), None); // Height out of bounds + assert_eq!(matrix.get(0, 2), None); // Width out of bounds + } + + #[test] + fn test_row_methods() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 4); // Height = 2, Width = 4 + let row: Vec<_> = matrix.row(1).unwrap().into_iter().collect(); + assert_eq!(row, vec![5, 6, 7, 8]); + unsafe { + let row: Vec<_> = matrix.row_unchecked(0).into_iter().collect(); + assert_eq!(row, vec![1, 2, 3, 4]); + let row: Vec<_> = matrix.row_subseq_unchecked(0, 0, 3).into_iter().collect(); + assert_eq!(row, vec![1, 2, 3]); + let row: Vec<_> = matrix.row_subseq_unchecked(0, 1, 3).into_iter().collect(); + assert_eq!(row, vec![2, 3]); + let row: Vec<_> = matrix.row_subseq_unchecked(0, 2, 4).into_iter().collect(); + assert_eq!(row, vec![3, 4]); + } + assert!(matrix.row(2).is_none()); // Height out of bounds + } + + #[test] + fn test_row_slice_methods() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9], 3); // Height = 3, Width = 3 + let slice0 = matrix.row_slice(0); + let slice2 = matrix.row_slice(2); + assert_eq!(slice0.unwrap().deref(), &[1, 2, 3]); + assert_eq!(slice2.unwrap().deref(), &[7, 8, 9]); + unsafe { + assert_eq!(&[1, 2, 3], matrix.row_slice_unchecked(0).deref()); + assert_eq!(&[7, 8, 9], matrix.row_slice_unchecked(2).deref()); + + assert_eq!(&[1, 2, 3], matrix.row_subslice_unchecked(0, 0, 3).deref()); + assert_eq!(&[8], matrix.row_subslice_unchecked(2, 1, 2).deref()); + } + assert!(matrix.row_slice(3).is_none()); // Height out of bounds + } + + #[test] + fn test_as_view() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); + let view = matrix.as_view(); + assert_eq!(view.values, &[1, 2, 3, 4]); + assert_eq!(view.width, 2); + } + + #[test] + fn test_as_view_mut() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); + let view = matrix.as_view_mut(); + view.values[0] = 10; + assert_eq!(matrix.values, vec![10, 2, 3, 4]); + } + + #[test] + fn test_copy_from() { + let mut matrix1 = RowMajorMatrix::new(vec![0, 0, 0, 0], 2); + let matrix2 = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); + matrix1.copy_from(&matrix2); + assert_eq!(matrix1.values, vec![1, 2, 3, 4]); + } + + #[test] + fn test_split_rows() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + let (top, bottom) = matrix.split_rows(1); + assert_eq!(top.values, vec![1, 2]); + assert_eq!(bottom.values, vec![3, 4, 5, 6]); + } + + #[test] + fn test_split_rows_mut() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + let (top, bottom) = matrix.split_rows_mut(1); + assert_eq!(top.values, vec![1, 2]); + assert_eq!(bottom.values, vec![3, 4, 5, 6]); + } + + #[test] + fn test_row_mut() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + matrix.row_mut(1)[0] = 10; + assert_eq!(matrix.values, vec![1, 2, 10, 4, 5, 6]); + } + + #[test] + fn test_bit_reversed_zero_pad() { + let matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + ], + 2, + ); + let padded = matrix.bit_reversed_zero_pad(1); + assert_eq!(padded.width, 2); + assert_eq!( + padded.values, + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(0), + BabyBear::new(0), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(0), + BabyBear::new(0) + ] + ); + } + + #[test] + fn test_bit_reversed_zero_pad_no_change() { + let matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + ], + 2, + ); + let padded = matrix.bit_reversed_zero_pad(0); + + assert_eq!(padded.width, 2); + assert_eq!( + padded.values, + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + ] + ); + } + + #[test] + fn test_scale() { + let mut matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + BabyBear::new(6), + ], + 2, + ); + matrix.scale(BabyBear::new(2)); + assert_eq!( + matrix.values, + vec![ + BabyBear::new(2), + BabyBear::new(4), + BabyBear::new(6), + BabyBear::new(8), + BabyBear::new(10), + BabyBear::new(12) + ] + ); + } + + #[test] + fn test_scale_row() { + let mut matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + BabyBear::new(6), + ], + 2, + ); + matrix.scale_row(1, BabyBear::new(3)); + assert_eq!( + matrix.values, + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(9), + BabyBear::new(12), + BabyBear::new(5), + BabyBear::new(6), + ] + ); + } + + #[test] + fn test_to_row_major_matrix() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + let converted = matrix.to_row_major_matrix(); + + // The converted matrix should have the same values and width + assert_eq!(converted.width, 2); + assert_eq!(converted.height(), 3); + assert_eq!(converted.values, vec![1, 2, 3, 4, 5, 6]); + } + + #[test] + fn test_horizontally_packed_row() { + type Packed = FieldArray; + + let matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + BabyBear::new(6), + ], + 3, + ); + + let (packed_iter, suffix_iter) = matrix.horizontally_packed_row::(1); + + let packed: Vec<_> = packed_iter.collect(); + let suffix: Vec<_> = suffix_iter.collect(); + + assert_eq!( + packed, + vec![Packed::from([BabyBear::new(4), BabyBear::new(5)])] + ); + assert_eq!(suffix, vec![BabyBear::new(6)]); + } + + #[test] + fn test_padded_horizontally_packed_row() { + use p3_baby_bear::BabyBear; + + type Packed = FieldArray; + + let matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + BabyBear::new(6), + ], + 3, + ); + + let packed_iter = matrix.padded_horizontally_packed_row::(1); + let packed: Vec<_> = packed_iter.collect(); + + assert_eq!( + packed, + vec![ + Packed::from([BabyBear::new(4), BabyBear::new(5)]), + Packed::from([BabyBear::new(6), BabyBear::new(0)]) + ] + ); + } + + #[test] + fn test_pad_to_height() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 3); + + // Original matrix: + // [ 1 2 3 ] + // [ 4 5 6 ] (height = 2) + + matrix.pad_to_height(4, 9); + + // Expected matrix after padding: + // [ 1 2 3 ] + // [ 4 5 6 ] + // [ 9 9 9 ] <-- Newly added row + // [ 9 9 9 ] <-- Newly added row + + assert_eq!(matrix.height(), 4); + assert_eq!(matrix.values, vec![1, 2, 3, 4, 5, 6, 9, 9, 9, 9, 9, 9]); + } + + #[test] + fn test_transpose_into() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 3); + + // Original matrix: + // [ 1 2 3 ] + // [ 4 5 6 ] + + let mut transposed = RowMajorMatrix::new(vec![0; 6], 2); + + matrix.transpose_into(&mut transposed); + + // Expected transposed matrix: + // [ 1 4 ] + // [ 2 5 ] + // [ 3 6 ] + + assert_eq!(transposed.width, 2); + assert_eq!(transposed.height(), 3); + assert_eq!(transposed.values, vec![1, 4, 2, 5, 3, 6]); + } + + #[test] + fn test_flatten_to_base() { + let matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + ], + 2, + ); + + let flattened: RowMajorMatrix = matrix.flatten_to_base(); + + assert_eq!(flattened.width, 2); + assert_eq!( + flattened.values, + vec![ + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + ] + ); + } + + #[test] + fn test_horizontally_packed_row_mut() { + type Packed = FieldArray; + + let mut matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + BabyBear::new(6), + ], + 3, + ); + + let (packed, suffix) = matrix.horizontally_packed_row_mut::(1); + packed[0] = Packed::from([BabyBear::new(9), BabyBear::new(10)]); + suffix[0] = BabyBear::new(11); + + assert_eq!( + matrix.values, + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(9), + BabyBear::new(10), + BabyBear::new(11), + ] + ); + } + + #[test] + fn test_par_row_chunks() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 2); + + let chunks: Vec<_> = matrix.par_row_chunks(2).collect(); + + assert_eq!(chunks.len(), 2); + assert_eq!(chunks[0].values, vec![1, 2, 3, 4]); + assert_eq!(chunks[1].values, vec![5, 6, 7, 8]); + } + + #[test] + fn test_par_row_chunks_exact() { + let matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + + let chunks: Vec<_> = matrix.par_row_chunks_exact(1).collect(); + + assert_eq!(chunks.len(), 3); + assert_eq!(chunks[0].values, vec![1, 2]); + assert_eq!(chunks[1].values, vec![3, 4]); + assert_eq!(chunks[2].values, vec![5, 6]); + } + + #[test] + fn test_par_row_chunks_mut() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8], 2); + + matrix + .par_row_chunks_mut(2) + .for_each(|chunk| chunk.values.iter_mut().for_each(|x| *x += 10)); + + assert_eq!(matrix.values, vec![11, 12, 13, 14, 15, 16, 17, 18]); + } + + #[test] + fn test_row_chunks_exact_mut() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + + for chunk in matrix.row_chunks_exact_mut(1) { + chunk.values.iter_mut().for_each(|x| *x *= 2); + } + + assert_eq!(matrix.values, vec![2, 4, 6, 8, 10, 12]); + } + + #[test] + fn test_par_row_chunks_exact_mut() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + + matrix + .par_row_chunks_exact_mut(1) + .for_each(|chunk| chunk.values.iter_mut().for_each(|x| *x += 5)); + + assert_eq!(matrix.values, vec![6, 7, 8, 9, 10, 11]); + } + + #[test] + fn test_row_pair_mut() { + let mut matrix = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 2); + + let (row1, row2) = matrix.row_pair_mut(0, 2); + row1[0] = 9; + row2[1] = 10; + + assert_eq!(matrix.values, vec![9, 2, 3, 4, 5, 10]); + } + + #[test] + fn test_packed_row_pair_mut() { + type Packed = FieldArray; + + let mut matrix = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(5), + BabyBear::new(6), + ], + 3, + ); + + let ((packed1, sfx1), (packed2, sfx2)) = matrix.packed_row_pair_mut::(0, 1); + packed1[0] = Packed::from([BabyBear::new(7), BabyBear::new(8)]); + packed2[0] = Packed::from([BabyBear::new(33), BabyBear::new(44)]); + sfx1[0] = BabyBear::new(99); + sfx2[0] = BabyBear::new(9); + + assert_eq!( + matrix.values, + vec![ + BabyBear::new(7), + BabyBear::new(8), + BabyBear::new(99), + BabyBear::new(33), + BabyBear::new(44), + BabyBear::new(9), + ] + ); + } + + #[test] + fn test_transpose_square_matrix() { + const START_INDEX: usize = 1; + const VALUE_LEN: usize = 9; + const WIDTH: usize = 3; + const HEIGHT: usize = 3; + + let matrix_values = (START_INDEX..=VALUE_LEN).collect::>(); + let matrix = RowMajorMatrix::new(matrix_values, WIDTH); + let transposed = matrix.transpose(); + let should_be_transposed_values = vec![1, 4, 7, 2, 5, 8, 3, 6, 9]; + let should_be_transposed = RowMajorMatrix::new(should_be_transposed_values, HEIGHT); + assert_eq!(transposed, should_be_transposed); + } + + #[test] + fn test_transpose_row_matrix() { + const START_INDEX: usize = 1; + const VALUE_LEN: usize = 30; + const WIDTH: usize = 1; + const HEIGHT: usize = 30; + + let matrix_values = (START_INDEX..=VALUE_LEN).collect::>(); + let matrix = RowMajorMatrix::new(matrix_values.clone(), WIDTH); + let transposed = matrix.transpose(); + let should_be_transposed = RowMajorMatrix::new(matrix_values, HEIGHT); + assert_eq!(transposed, should_be_transposed); + } + + #[test] + fn test_transpose_rectangular_matrix() { + const START_INDEX: usize = 1; + const VALUE_LEN: usize = 30; + const WIDTH: usize = 5; + const HEIGHT: usize = 6; + + let matrix_values = (START_INDEX..=VALUE_LEN).collect::>(); + let matrix = RowMajorMatrix::new(matrix_values, WIDTH); + let transposed = matrix.transpose(); + let should_be_transposed_values = vec![ + 1, 6, 11, 16, 21, 26, 2, 7, 12, 17, 22, 27, 3, 8, 13, 18, 23, 28, 4, 9, 14, 19, 24, 29, + 5, 10, 15, 20, 25, 30, + ]; + let should_be_transposed = RowMajorMatrix::new(should_be_transposed_values, HEIGHT); + assert_eq!(transposed, should_be_transposed); + } + + #[test] + fn test_transpose_larger_rectangular_matrix() { + const START_INDEX: usize = 1; + const VALUE_LEN: usize = 131072; // 512 * 256 + const WIDTH: usize = 256; + const HEIGHT: usize = 512; + + let matrix_values = (START_INDEX..=VALUE_LEN).collect::>(); + let matrix = RowMajorMatrix::new(matrix_values, WIDTH); + let transposed = matrix.transpose(); + + assert_eq!(transposed.width(), HEIGHT); + assert_eq!(transposed.height(), WIDTH); + + for col_index in 0..WIDTH { + for row_index in 0..HEIGHT { + assert_eq!( + matrix.values[row_index * WIDTH + col_index], + transposed.values[col_index * HEIGHT + row_index] + ); + } + } + } + + #[test] + fn test_transpose_very_large_rectangular_matrix() { + const START_INDEX: usize = 1; + const VALUE_LEN: usize = 1048576; // 512 * 256 + const WIDTH: usize = 1024; + const HEIGHT: usize = 1024; + + let matrix_values = (START_INDEX..=VALUE_LEN).collect::>(); + let matrix = RowMajorMatrix::new(matrix_values, WIDTH); + let transposed = matrix.transpose(); + + assert_eq!(transposed.width(), HEIGHT); + assert_eq!(transposed.height(), WIDTH); + + for col_index in 0..WIDTH { + for row_index in 0..HEIGHT { + assert_eq!( + matrix.values[row_index * WIDTH + col_index], + transposed.values[col_index * HEIGHT + row_index] + ); + } + } + } + + #[test] + fn test_vertically_packed_row_pair() { + type Packed = FieldArray; + + let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::>(), 4); + + // Calling the function with r = 0 and step = 2 + let packed = matrix.vertically_packed_row_pair::(0, 2); + + // Matrix visualization: + // + // [ 1 2 3 4 ] <-- Row 0 + // [ 5 6 7 8 ] <-- Row 1 + // [ 9 10 11 12 ] <-- Row 2 + // [ 13 14 15 16 ] <-- Row 3 + // + // Packing rows 0-1 together, then rows 2-3 together: + // + // Packed result: + // [ + // (1, 5), (2, 6), (3, 7), (4, 8), // First packed row (Row 0 & Row 1) + // (9, 13), (10, 14), (11, 15), (12, 16), // Second packed row (Row 2 & Row 3) + // ] + + assert_eq!( + packed, + (1..5) + .chain(9..13) + .map(|i| [BabyBear::new(i), BabyBear::new(i + 4)].into()) + .collect::>(), + ); + } + + #[test] + fn test_vertically_packed_row_pair_overlap() { + type Packed = FieldArray; + + let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::>(), 4); + + // Original matrix visualization: + // + // [ 1 2 3 4 ] <-- Row 0 + // [ 5 6 7 8 ] <-- Row 1 + // [ 9 10 11 12 ] <-- Row 2 + // [ 13 14 15 16 ] <-- Row 3 + // + // Packing rows 0-1 together, then rows 1-2 together: + // + // Expected packed result: + // [ + // (1, 5), (2, 6), (3, 7), (4, 8), // First packed row (Row 0 & Row 1) + // (5, 9), (6, 10), (7, 11), (8, 12) // Second packed row (Row 1 & Row 2) + // ] + + // Calling the function with overlapping rows (r = 0 and step = 1) + let packed = matrix.vertically_packed_row_pair::(0, 1); + + assert_eq!( + packed, + (1..5) + .chain(5..9) + .map(|i| [BabyBear::new(i), BabyBear::new(i + 4)].into()) + .collect::>(), + ); + } + + #[test] + fn test_vertically_packed_row_pair_wraparound_start_1() { + use p3_baby_bear::BabyBear; + use p3_field::FieldArray; + + type Packed = FieldArray; + + let matrix = RowMajorMatrix::new((1..17).map(BabyBear::new).collect::>(), 4); + + // Original matrix visualization: + // + // [ 1 2 3 4 ] <-- Row 0 + // [ 5 6 7 8 ] <-- Row 1 + // [ 9 10 11 12 ] <-- Row 2 + // [ 13 14 15 16 ] <-- Row 3 + // + // Packing starts from row 1, skipping 2 rows (step = 2): + // - The first packed row should contain row 1 & row 2. + // - The second packed row should contain row 3 & row 1 (wraparound case). + // + // Expected packed result: + // [ + // (5, 9), (6, 10), (7, 11), (8, 12), // Packed row (Row 1 & Row 2) + // (13, 1), (14, 2), (15, 3), (16, 4) // Packed row (Row 3 & Row 1) + // ] + + // Calling the function with wraparound scenario (starting at r = 1 with step = 2) + let packed = matrix.vertically_packed_row_pair::(1, 2); + + assert_eq!( + packed, + vec![ + Packed::from([BabyBear::new(5), BabyBear::new(9)]), + Packed::from([BabyBear::new(6), BabyBear::new(10)]), + Packed::from([BabyBear::new(7), BabyBear::new(11)]), + Packed::from([BabyBear::new(8), BabyBear::new(12)]), + Packed::from([BabyBear::new(13), BabyBear::new(1)]), + Packed::from([BabyBear::new(14), BabyBear::new(2)]), + Packed::from([BabyBear::new(15), BabyBear::new(3)]), + Packed::from([BabyBear::new(16), BabyBear::new(4)]), + ] + ); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/extension.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/extension.rs new file mode 100644 index 00000000..dc2bf6ba --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/extension.rs @@ -0,0 +1,328 @@ +use alloc::vec::Vec; +use core::iter; +use core::marker::PhantomData; +use core::ops::Deref; + +use p3_field::{ExtensionField, Field}; + +use crate::Matrix; + +/// A view that flattens a matrix of extension field elements into a matrix of base field elements. +/// +/// Each element of the original matrix is an extension field element `EF`, composed of several +/// base field elements `F`. This view expands each `EF` element into its base field components, +/// effectively increasing the number of columns (width) while keeping the number of rows unchanged. +#[derive(Debug)] +pub struct FlatMatrixView(Inner, PhantomData<(F, EF)>); + +impl FlatMatrixView { + pub const fn new(inner: Inner) -> Self { + Self(inner, PhantomData) + } +} + +impl Deref for FlatMatrixView { + type Target = Inner; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Matrix for FlatMatrixView +where + F: Field, + EF: ExtensionField, + Inner: Matrix, +{ + fn width(&self) -> usize { + self.0.width() * EF::DIMENSION + } + + fn height(&self) -> usize { + self.0.height() + } + + unsafe fn get_unchecked(&self, r: usize, c: usize) -> F { + // The c'th base field element in a row of extension field elements is + // at index c % EF::DIMENSION in the c / EF::DIMENSION'th extension element. + let c_inner = c / EF::DIMENSION; + let inner = unsafe { + // Safety: The caller must ensure that r < self.height() and c < self.width(). + // Assuming this, c / EF::DIMENSION < self.0.width(). + self.0.get_unchecked(r, c_inner) + }; + inner.as_basis_coefficients_slice()[c % EF::DIMENSION] + } + + unsafe fn row_unchecked( + &self, + r: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height(). + FlatIter { + inner: self.0.row_unchecked(r).into_iter().peekable(), + idx: 0, + _phantom: PhantomData, + } + } + } + + unsafe fn row_subseq_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl IntoIterator + Send + Sync> { + // We can skip the first start / EF::DIMENSION elements in the row. + let len = end - start; + let inner_start = start / EF::DIMENSION; + unsafe { + // Safety: The caller must ensure that r < self.height(), start <= end and end < self.width(). + FlatIter { + inner: self + .0 + // We set end to be the width of the inner matrix and use take to ensure we get the right + // number of elements. + .row_subseq_unchecked(r, inner_start, self.0.width()) + .into_iter() + .peekable(), + idx: start, + _phantom: PhantomData, + } + .take(len) + } + } + + unsafe fn row_slice_unchecked(&self, r: usize) -> impl Deref { + unsafe { + // Safety: The caller must ensure that r < self.height(). + self.0 + .row_slice_unchecked(r) + .iter() + .flat_map(|val| val.as_basis_coefficients_slice()) + .copied() + .collect::>() + } + } +} + +pub struct FlatIter { + inner: iter::Peekable, + idx: usize, + _phantom: PhantomData, +} + +impl Iterator for FlatIter +where + F: Field, + EF: ExtensionField, + I: Iterator, +{ + type Item = F; + fn next(&mut self) -> Option { + if self.idx == EF::DIMENSION { + self.idx = 0; + self.inner.next(); + } + let value = self.inner.peek()?.as_basis_coefficients_slice()[self.idx]; + self.idx += 1; + Some(value) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use itertools::Itertools; + use p3_field::extension::Complex; + use p3_field::{BasedVectorSpace, PrimeCharacteristicRing}; + use p3_mersenne_31::Mersenne31; + + use super::*; + use crate::dense::RowMajorMatrix; + type F = Mersenne31; + type EF = Complex; + + #[test] + fn flat_matrix() { + let values = vec![ + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 10)), + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 20)), + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 30)), + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 40)), + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 50)), + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 60)), + ]; + let ext = RowMajorMatrix::::new(values, 2); + let flat = FlatMatrixView::::new(ext); + + assert_eq!(flat.width(), 4); + assert_eq!(flat.height(), 3); + + assert_eq!(flat.get(0, 2), Some(F::from_u8(20))); + assert_eq!(flat.get(1, 3), Some(F::from_u8(41))); + assert_eq!(flat.get(2, 0), Some(F::from_u8(50))); + + unsafe { + assert_eq!(flat.get_unchecked(0, 1), F::from_u8(11)); + assert_eq!(flat.get_unchecked(1, 0), F::from_u8(30)); + assert_eq!(flat.get_unchecked(2, 2), F::from_u8(60)); + } + + assert_eq!( + &*flat.row_slice(0).unwrap(), + &[10, 11, 20, 21].map(F::from_u8) + ); + unsafe { + assert_eq!( + &*flat.row_slice_unchecked(1), + &[30, 31, 40, 41].map(F::from_u8) + ); + assert_eq!( + &*flat.row_subslice_unchecked(2, 0, 3), + &[50, 51, 60].map(F::from_u8) + ); + } + + assert_eq!( + flat.row(2).unwrap().into_iter().collect_vec(), + [50, 51, 60, 61].map(F::from_u8) + ); + unsafe { + assert_eq!( + flat.row_unchecked(1).into_iter().collect_vec(), + [30, 31, 40, 41].map(F::from_u8) + ); + assert_eq!( + flat.row_subseq_unchecked(0, 1, 4).into_iter().collect_vec(), + [11, 20, 21].map(F::from_u8) + ); + } + + assert!(flat.get(0, 4).is_none()); // Width out of bounds + assert!(flat.get(3, 0).is_none()); // Height out of bounds + assert!(flat.row(3).is_none()); // Height out of bounds + assert!(flat.row_slice(3).is_none()); // Height out of bounds + } + + #[test] + fn test_flat_matrix_width() { + // Create a 2-column, 2-row matrix of EF elements. + // Each EF element expands to EF::DIMENSION base field elements when flattened. + // Therefore, the flattened width should be 2 * EF::DIMENSION. + let matrix = RowMajorMatrix::::new(vec![EF::default(); 4], 2); + let flat = FlatMatrixView::::new(matrix); + assert_eq!(flat.width(), 2 * >::DIMENSION); + } + + #[test] + fn test_flat_matrix_height() { + // Construct a 3-column matrix with 6 EF elements (2 rows). + // The flattened view should preserve the original number of rows. + let matrix = RowMajorMatrix::::new(vec![EF::default(); 6], 3); + let flat = FlatMatrixView::::new(matrix); + assert_eq!(flat.height(), 2); + } + + #[test] + fn test_flat_matrix_row_iterator() { + // Create a single row of two EF elements: + // First EF = [1, 2], second EF = [10, 11] (in base field representation). + let values = vec![ + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 1)), + EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + 10)), + ]; + let matrix = RowMajorMatrix::new(values, 2); + let flat = FlatMatrixView::::new(matrix); + + // Flattened row should concatenate basis coefficients of both EF elements. + let row: Vec<_> = flat.first_row().unwrap().into_iter().collect(); + let expected = [1, 2, 10, 11].map(F::from_u8).to_vec(); + + assert_eq!(row, expected); + } + + #[test] + fn test_flat_matrix_row_slice_correctness() { + // Construct a row with two EF values: [1, 2] and [10, 11]. + // Verify that row_slice() correctly returns a flat &[F] of base field values. + let ef = |offset| EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + offset)); + let matrix = RowMajorMatrix::new(vec![ef(1), ef(10)], 2); + let flat = FlatMatrixView::::new(matrix); + + assert_eq!( + &*flat.row_slice(0).unwrap(), + &[1, 2, 10, 11].map(F::from_u8) + ); + } + + #[test] + fn test_flat_matrix_empty() { + // Edge case: test behavior on empty matrix. + // Expect zero width and height in the flattened view. + let matrix = RowMajorMatrix::::new(vec![], 0); + let flat = FlatMatrixView::::new(matrix); + + assert_eq!(flat.height(), 0); + assert_eq!(flat.width(), 0); + } + + #[test] + fn test_flat_iter_length_and_values() { + // Create a row with three EF values, each with offset base coefficients: + // [0,1], [10,11], [20,21] -> flattened row should be [0,1,10,11,20,21]. + let ef = |offset| EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + offset)); + let values = vec![ef(0), ef(10), ef(20)]; + let matrix = RowMajorMatrix::new(values, 3); // 1 row + let flat = FlatMatrixView::::new(matrix); + + let row: Vec<_> = flat.first_row().unwrap().into_iter().collect(); + let expected = [0, 1, 10, 11, 20, 21].map(F::from_u8).to_vec(); + assert_eq!(row, expected); + } + + #[test] + fn test_flat_matrix_multiple_rows() { + // Construct a 2-column, 2-row matrix of EF values, with varying offsets per row. + // Row 0: [0,1], [10,11]; Row 1: [20,21], [30,31]. + // Verify that the flattening preserves row structure and ordering. + let ef = |base| EF::from_basis_coefficients_fn(|i| F::from_u8(base + i as u8)); + let matrix = RowMajorMatrix::new(vec![ef(0), ef(10), ef(20), ef(30)], 2); + let flat = FlatMatrixView::::new(matrix); + + let row0: Vec<_> = flat.first_row().unwrap().into_iter().collect(); + let row1: Vec<_> = flat.row(1).unwrap().into_iter().collect(); + + assert_eq!(row0, [0, 1, 10, 11].map(F::from_u8).to_vec()); + assert_eq!(row1, [20, 21, 30, 31].map(F::from_u8).to_vec()); + } + + #[test] + fn test_flat_iter_yields_across_multiple_efs() { + // Build 1 row with 3 EF elements: + // - ef(0) = [0, 1] + // - ef(10) = [10, 11] + // - ef(20) = [20, 21] + // + // The flattened row should yield: + // [0, 1, 10, 11, 20, 21] as base field elements (F) + let ef = |offset| EF::from_basis_coefficients_fn(|i| F::from_u8(i as u8 + offset)); + let matrix = RowMajorMatrix::new(vec![ef(0), ef(10), ef(20)], 3); // 1 row, 3 EF elements + let flat = FlatMatrixView::::new(matrix); + + let mut row_iter = flat.row(0).unwrap().into_iter(); + + // Expected flattened result + let expected = [0, 1, 10, 11, 20, 21].map(F::from_u8); + + for expected_val in expected { + assert_eq!(row_iter.next(), Some(expected_val)); + } + + // Iterator should now be exhausted + assert_eq!(row_iter.next(), None); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/horizontally_truncated.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/horizontally_truncated.rs new file mode 100644 index 00000000..a606e2a1 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/horizontally_truncated.rs @@ -0,0 +1,237 @@ +use core::marker::PhantomData; + +use crate::Matrix; + +/// A matrix wrapper that limits the number of columns visible from an inner matrix. +/// +/// This struct wraps another matrix and restricts access to only the first `truncated_width` columns. +pub struct HorizontallyTruncated { + /// The underlying full matrix being wrapped. + inner: Inner, + /// The number of columns to expose from the inner matrix. + truncated_width: usize, + /// Marker for the element type `T`, not used at runtime. + _phantom: PhantomData, +} + +impl> HorizontallyTruncated +where + T: Send + Sync + Clone, +{ + /// Construct a new horizontally truncated view of a matrix. + /// + /// # Arguments + /// - `inner`: The full inner matrix to be wrapped. + /// - `truncated_width`: The number of columns to expose (must be ≤ `inner.width()`). + /// + /// Returns `None` if `truncated_width` is greater than the width of the inner matrix. + pub fn new(inner: Inner, truncated_width: usize) -> Option { + (truncated_width <= inner.width()).then(|| Self { + inner, + truncated_width, + _phantom: PhantomData, + }) + } +} + +impl Matrix for HorizontallyTruncated +where + T: Send + Sync + Clone, + Inner: Matrix, +{ + /// Returns the number of columns exposed by the truncated matrix. + #[inline(always)] + fn width(&self) -> usize { + self.truncated_width + } + + /// Returns the number of rows in the matrix (same as the inner matrix). + #[inline(always)] + fn height(&self) -> usize { + self.inner.height() + } + + #[inline(always)] + unsafe fn get_unchecked(&self, r: usize, c: usize) -> T { + unsafe { + // Safety: The caller must ensure that `c < truncated_width` and `r < self.height()`. + self.inner.get_unchecked(r, c) + } + } + + unsafe fn row_unchecked( + &self, + r: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that `r < self.height()`. + self.inner.row_subseq_unchecked(r, 0, self.truncated_width) + } + } + + unsafe fn row_subseq_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height() and start <= end <= self.width(). + self.inner.row_subseq_unchecked(r, start, end) + } + } + + unsafe fn row_subslice_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl core::ops::Deref { + unsafe { + // Safety: The caller must ensure that `r < self.height()` and `start <= end <= self.width()`. + self.inner.row_subslice_unchecked(r, start, end) + } + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + use alloc::vec::Vec; + + use super::*; + use crate::dense::RowMajorMatrix; + + #[test] + fn test_truncate_width_by_one() { + // Create a 3x4 matrix: + // [ 1 2 3 4] + // [ 5 6 7 8] + // [ 9 10 11 12] + let inner = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 4); + + // Truncate to width 3. + let truncated = HorizontallyTruncated::new(inner, 3).unwrap(); + + // Width should be 3. + assert_eq!(truncated.width(), 3); + + // Height remains unchanged. + assert_eq!(truncated.height(), 3); + + // Check individual elements. + assert_eq!(truncated.get(0, 0), Some(1)); // row 0, col 0 + assert_eq!(truncated.get(1, 1), Some(6)); // row 1, col 1 + unsafe { + assert_eq!(truncated.get_unchecked(0, 1), 2); // row 0, col 1 + assert_eq!(truncated.get_unchecked(2, 2), 11); // row 1, col 0 + } + + // Row 0: should return [1, 2, 3] + let row0: Vec<_> = truncated.row(0).unwrap().into_iter().collect(); + assert_eq!(row0, vec![1, 2, 3]); + unsafe { + // Row 2: should return [5, 6, 7] + let row1: Vec<_> = truncated.row_unchecked(1).into_iter().collect(); + assert_eq!(row1, vec![5, 6, 7]); + + // Row 3: is equal to return [9, 10, 11] + let row3_subset: Vec<_> = truncated + .row_subseq_unchecked(2, 1, 2) + .into_iter() + .collect(); + assert_eq!(row3_subset, vec![10]); + } + + unsafe { + let row1 = truncated.row_slice(1).unwrap(); + assert_eq!(&*row1, &[5, 6, 7]); + + let row2 = truncated.row_slice_unchecked(2); + assert_eq!(&*row2, &[9, 10, 11]); + + let row0_subslice = truncated.row_subslice_unchecked(0, 0, 2); + assert_eq!(&*row0_subslice, &[1, 2]); + } + + assert!(truncated.get(0, 3).is_none()); // Width out of bounds + assert!(truncated.get(3, 0).is_none()); // Height out of bounds + assert!(truncated.row(3).is_none()); // Height out of bounds + assert!(truncated.row_slice(3).is_none()); // Height out of bounds + + // Convert the truncated view to a RowMajorMatrix and check contents. + let as_matrix = truncated.to_row_major_matrix(); + + // The expected matrix after truncation: + // [1 2 3] + // [5 6 7] + // [9 10 11] + let expected = RowMajorMatrix::new(vec![1, 2, 3, 5, 6, 7, 9, 10, 11], 3); + + assert_eq!(as_matrix, expected); + } + + #[test] + fn test_no_truncation() { + // 2x2 matrix: + // [ 7 8 ] + // [ 9 10 ] + let inner = RowMajorMatrix::new(vec![7, 8, 9, 10], 2); + + // Truncate to full width (no change). + let truncated = HorizontallyTruncated::new(inner, 2).unwrap(); + + assert_eq!(truncated.width(), 2); + assert_eq!(truncated.height(), 2); + assert_eq!(truncated.get(0, 1).unwrap(), 8); + assert_eq!(truncated.get(1, 0).unwrap(), 9); + + unsafe { + assert_eq!(truncated.get_unchecked(0, 0), 7); + assert_eq!(truncated.get_unchecked(1, 1), 10); + } + + let row0: Vec<_> = truncated.row(0).unwrap().into_iter().collect(); + assert_eq!(row0, vec![7, 8]); + + let row1: Vec<_> = unsafe { truncated.row_unchecked(1).into_iter().collect() }; + assert_eq!(row1, vec![9, 10]); + + assert!(truncated.get(0, 2).is_none()); // Width out of bounds + assert!(truncated.get(2, 0).is_none()); // Height out of bounds + assert!(truncated.row(2).is_none()); // Height out of bounds + assert!(truncated.row_slice(2).is_none()); // Height out of bounds + } + + #[test] + fn test_truncate_to_zero_width() { + // 1x3 matrix: [11 12 13] + let inner = RowMajorMatrix::new(vec![11, 12, 13], 3); + + // Truncate to width 0. + let truncated = HorizontallyTruncated::new(inner, 0).unwrap(); + + assert_eq!(truncated.width(), 0); + assert_eq!(truncated.height(), 1); + + // Row should be empty. + let row: Vec<_> = truncated.row(0).unwrap().into_iter().collect(); + assert!(row.is_empty()); + + assert!(truncated.get(0, 0).is_none()); // Width out of bounds + assert!(truncated.get(1, 0).is_none()); // Height out of bounds + assert!(truncated.row(1).is_none()); // Height out of bounds + assert!(truncated.row_slice(1).is_none()); // Height out of bounds + } + + #[test] + fn test_invalid_truncation_width() { + // 2x2 matrix: + // [1 2] + // [3 4] + let inner = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); + + // Attempt to truncate beyond inner width (invalid). + assert!(HorizontallyTruncated::new(inner, 5).is_none()); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/lib.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/lib.rs new file mode 100644 index 00000000..b22c597a --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/lib.rs @@ -0,0 +1,722 @@ +//! Matrix library. + +#![no_std] + +extern crate alloc; + +use alloc::vec::Vec; +use core::fmt::{Debug, Display, Formatter}; +use core::ops::Deref; + +use itertools::{Itertools, izip}; +use p3_field::{ + BasedVectorSpace, ExtensionField, Field, PackedValue, PrimeCharacteristicRing, dot_product, +}; +use p3_maybe_rayon::prelude::*; +use strided::{VerticallyStridedMatrixView, VerticallyStridedRowIndexMap}; + +use crate::dense::RowMajorMatrix; + +pub mod bitrev; +pub mod dense; +pub mod extension; +pub mod horizontally_truncated; +pub mod row_index_mapped; +pub mod stack; +pub mod strided; +pub mod util; + +/// A simple struct representing the shape of a matrix. +/// +/// The `Dimensions` type stores the number of columns (`width`) and rows (`height`) +/// of a matrix. It is commonly used for querying and displaying matrix shapes. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Dimensions { + /// Number of columns in the matrix. + pub width: usize, + /// Number of rows in the matrix. + pub height: usize, +} + +impl Debug for Dimensions { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}x{}", self.width, self.height) + } +} + +impl Display for Dimensions { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}x{}", self.width, self.height) + } +} + +/// A generic trait for two-dimensional matrix-like data structures. +/// +/// The `Matrix` trait provides a uniform interface for accessing rows, elements, +/// and computing with matrices in both sequential and parallel contexts. It supports +/// packing strategies for SIMD optimizations and interaction with extension fields. +pub trait Matrix: Send + Sync { + /// Returns the number of columns in the matrix. + fn width(&self) -> usize; + + /// Returns the number of rows in the matrix. + fn height(&self) -> usize; + + /// Returns the dimensions (width, height) of the matrix. + fn dimensions(&self) -> Dimensions { + Dimensions { + width: self.width(), + height: self.height(), + } + } + + // The methods: + // get, get_unchecked, row, row_unchecked, row_subseq_unchecked, row_slice, row_slice_unchecked, row_subslice_unchecked + // are all defined in a circular manner so you only need to implement a subset of them. + // In particular is is enough to implement just one of: row_unchecked, row_subseq_unchecked + // + // That being said, most implementations will want to implement several methods for performance reasons. + + /// Returns the element at the given row and column. + /// + /// Returns `None` if either `r >= height()` or `c >= width()`. + #[inline] + fn get(&self, r: usize, c: usize) -> Option { + (r < self.height() && c < self.width()).then(|| unsafe { + // Safety: Clearly `r < self.height()` and `c < self.width()`. + self.get_unchecked(r, c) + }) + } + + /// Returns the element at the given row and column. + /// + /// For a safe alternative, see [`get`]. + /// + /// # Safety + /// The caller must ensure that `r < self.height()` and `c < self.width()`. + /// Breaking any of these assumptions is considered undefined behaviour. + #[inline] + unsafe fn get_unchecked(&self, r: usize, c: usize) -> T { + unsafe { self.row_slice_unchecked(r)[c].clone() } + } + + /// Returns an iterator over the elements of the `r`-th row. + /// + /// The iterator will have `self.width()` elements. + /// + /// Returns `None` if `r >= height()`. + #[inline] + fn row( + &self, + r: usize, + ) -> Option + Send + Sync>> { + (r < self.height()).then(|| unsafe { + // Safety: Clearly `r < self.height()`. + self.row_unchecked(r) + }) + } + + /// Returns an iterator over the elements of the `r`-th row. + /// + /// The iterator will have `self.width()` elements. + /// + /// For a safe alternative, see [`row`]. + /// + /// # Safety + /// The caller must ensure that `r < self.height()`. + /// Breaking this assumption is considered undefined behaviour. + #[inline] + unsafe fn row_unchecked( + &self, + r: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { self.row_subseq_unchecked(r, 0, self.width()) } + } + + /// Returns an iterator over the elements of the `r`-th row from position `start` to `end`. + /// + /// When `start = 0` and `end = width()`, this is equivalent to [`row_unchecked`]. + /// + /// For a safe alternative, use [`row`], along with the `skip` and `take` iterator methods. + /// + /// # Safety + /// The caller must ensure that `r < self.height()` and `start <= end <= self.width()`. + /// Breaking any of these assumptions is considered undefined behaviour. + #[inline] + unsafe fn row_subseq_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + self.row_unchecked(r) + .into_iter() + .skip(start) + .take(end - start) + } + } + + /// Returns the elements of the `r`-th row as something which can be coerced to a slice. + /// + /// Returns `None` if `r >= height()`. + #[inline] + fn row_slice(&self, r: usize) -> Option> { + (r < self.height()).then(|| unsafe { + // Safety: Clearly `r < self.height()`. + self.row_slice_unchecked(r) + }) + } + + /// Returns the elements of the `r`-th row as something which can be coerced to a slice. + /// + /// For a safe alternative, see [`row_slice`]. + /// + /// # Safety + /// The caller must ensure that `r < self.height()`. + /// Breaking this assumption is considered undefined behaviour. + #[inline] + unsafe fn row_slice_unchecked(&self, r: usize) -> impl Deref { + unsafe { self.row_subslice_unchecked(r, 0, self.width()) } + } + + /// Returns a subset of elements of the `r`-th row as something which can be coerced to a slice. + /// + /// When `start = 0` and `end = width()`, this is equivalent to [`row_slice_unchecked`]. + /// + /// For a safe alternative, see [`row_slice`]. + /// + /// # Safety + /// The caller must ensure that `r < self.height()` and `start <= end <= self.width()`. + /// Breaking any of these assumptions is considered undefined behaviour. + #[inline] + unsafe fn row_subslice_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl Deref { + unsafe { + self.row_subseq_unchecked(r, start, end) + .into_iter() + .collect_vec() + } + } + + /// Returns an iterator over all rows in the matrix. + #[inline] + fn rows(&self) -> impl Iterator> + Send + Sync { + unsafe { + // Safety: `r` always satisfies `r < self.height()`. + (0..self.height()).map(move |r| self.row_unchecked(r).into_iter()) + } + } + + /// Returns a parallel iterator over all rows in the matrix. + #[inline] + fn par_rows( + &self, + ) -> impl IndexedParallelIterator> + Send + Sync { + unsafe { + // Safety: `r` always satisfies `r < self.height()`. + (0..self.height()) + .into_par_iter() + .map(move |r| self.row_unchecked(r).into_iter()) + } + } + + /// Collect the elements of the rows `r` through `r + c`. If anything is larger than `self.height()` + /// simply wrap around to the beginning of the matrix. + fn wrapping_row_slices(&self, r: usize, c: usize) -> Vec> { + unsafe { + // Safety: Thank to the `%`, the rows index is always less than `self.height()`. + (0..c) + .map(|i| self.row_slice_unchecked((r + i) % self.height())) + .collect_vec() + } + } + + /// Returns an iterator over the first row of the matrix. + /// + /// Returns None if `height() == 0`. + #[inline] + fn first_row( + &self, + ) -> Option + Send + Sync>> { + self.row(0) + } + + /// Returns an iterator over the last row of the matrix. + /// + /// Returns None if `height() == 0`. + #[inline] + fn last_row( + &self, + ) -> Option + Send + Sync>> { + if self.height() == 0 { + None + } else { + // Safety: Clearly `self.height() - 1 < self.height()`. + unsafe { Some(self.row_unchecked(self.height() - 1)) } + } + } + + /// Converts the matrix into a `RowMajorMatrix` by collecting all rows into a single vector. + fn to_row_major_matrix(self) -> RowMajorMatrix + where + Self: Sized, + T: Clone, + { + RowMajorMatrix::new(self.rows().flatten().collect(), self.width()) + } + + /// Get a packed iterator over the `r`-th row. + /// + /// If the row length is not divisible by the packing width, the final elements + /// are returned as a base iterator with length `<= P::WIDTH - 1`. + /// + /// # Panics + /// Panics if `r >= height()`. + fn horizontally_packed_row<'a, P>( + &'a self, + r: usize, + ) -> ( + impl Iterator + Send + Sync, + impl Iterator + Send + Sync, + ) + where + P: PackedValue, + T: Clone + 'a, + { + assert!(r < self.height(), "Row index out of bounds."); + let num_packed = self.width() / P::WIDTH; + unsafe { + // Safety: We have already checked that `r < height()`. + let mut iter = self + .row_subseq_unchecked(r, 0, num_packed * P::WIDTH) + .into_iter(); + + // array::from_fn is guaranteed to always call in order. + let packed = + (0..num_packed).map(move |_| P::from_fn(|_| iter.next().unwrap_unchecked())); + + let sfx = self + .row_subseq_unchecked(r, num_packed * P::WIDTH, self.width()) + .into_iter(); + (packed, sfx) + } + } + + /// Get a packed iterator over the `r`-th row. + /// + /// If the row length is not divisible by the packing width, the final entry will be zero-padded. + /// + /// # Panics + /// Panics if `r >= height()`. + fn padded_horizontally_packed_row<'a, P>( + &'a self, + r: usize, + ) -> impl Iterator + Send + Sync + where + P: PackedValue, + T: Clone + Default + 'a, + { + let mut row_iter = self.row(r).expect("Row index out of bounds.").into_iter(); + let num_elems = self.width().div_ceil(P::WIDTH); + // array::from_fn is guaranteed to always call in order. + (0..num_elems).map(move |_| P::from_fn(|_| row_iter.next().unwrap_or_default())) + } + + /// Get a parallel iterator over all packed rows of the matrix. + /// + /// If the matrix width is not divisible by the packing width, the final elements + /// of each row are returned as a base iterator with length `<= P::WIDTH - 1`. + fn par_horizontally_packed_rows<'a, P>( + &'a self, + ) -> impl IndexedParallelIterator< + Item = ( + impl Iterator + Send + Sync, + impl Iterator + Send + Sync, + ), + > + where + P: PackedValue, + T: Clone + 'a, + { + (0..self.height()) + .into_par_iter() + .map(|r| self.horizontally_packed_row(r)) + } + + /// Get a parallel iterator over all packed rows of the matrix. + /// + /// If the matrix width is not divisible by the packing width, the final entry of each row will be zero-padded. + fn par_padded_horizontally_packed_rows<'a, P>( + &'a self, + ) -> impl IndexedParallelIterator + Send + Sync> + where + P: PackedValue, + T: Clone + Default + 'a, + { + (0..self.height()) + .into_par_iter() + .map(|r| self.padded_horizontally_packed_row(r)) + } + + /// Pack together a collection of adjacent rows from the matrix. + /// + /// Returns an iterator whose i'th element is packing of the i'th element of the + /// rows r through r + P::WIDTH - 1. If we exceed the height of the matrix, + /// wrap around and include initial rows. + #[inline] + fn vertically_packed_row

(&self, r: usize) -> impl Iterator + where + T: Copy, + P: PackedValue, + { + // Precompute row slices once to minimize redundant calls and improve performance. + let rows = self.wrapping_row_slices(r, P::WIDTH); + + // Using precomputed rows avoids repeatedly calling `row_slice`, which is costly. + (0..self.width()).map(move |c| P::from_fn(|i| rows[i][c])) + } + + /// Pack together a collection of rows and "next" rows from the matrix. + /// + /// Returns a vector corresponding to 2 packed rows. The i'th element of the first + /// row contains the packing of the i'th element of the rows r through r + P::WIDTH - 1. + /// The i'th element of the second row contains the packing of the i'th element of the + /// rows r + step through r + step + P::WIDTH - 1. If at some point we exceed the + /// height of the matrix, wrap around and include initial rows. + #[inline] + fn vertically_packed_row_pair

(&self, r: usize, step: usize) -> Vec

+ where + T: Copy, + P: PackedValue, + { + // Whilst it would appear that this can be replaced by two calls to vertically_packed_row + // tests seem to indicate that combining them in the same function is slightly faster. + // It's probably allowing the compiler to make some optimizations on the fly. + + let rows = self.wrapping_row_slices(r, P::WIDTH); + let next_rows = self.wrapping_row_slices(r + step, P::WIDTH); + + (0..self.width()) + .map(|c| P::from_fn(|i| rows[i][c])) + .chain((0..self.width()).map(|c| P::from_fn(|i| next_rows[i][c]))) + .collect_vec() + } + + /// Returns a view over a vertically strided submatrix. + /// + /// The view selects rows using `r = offset + i * stride` for each `i`. + fn vertically_strided(self, stride: usize, offset: usize) -> VerticallyStridedMatrixView + where + Self: Sized, + { + VerticallyStridedRowIndexMap::new_view(self, stride, offset) + } + + /// Compute Mᵀv, aka premultiply this matrix by the given vector, + /// aka scale each row by the corresponding entry in `v` and take the sum across rows. + /// `v` can be a vector of extension elements. + fn columnwise_dot_product(&self, v: &[EF]) -> Vec + where + T: Field, + EF: ExtensionField, + { + let packed_width = self.width().div_ceil(T::Packing::WIDTH); + + let packed_result = self + .par_padded_horizontally_packed_rows::() + .zip(v) + .par_fold_reduce( + || EF::ExtensionPacking::zero_vec(packed_width), + |mut acc, (row, &scale)| { + let scale = EF::ExtensionPacking::from_basis_coefficients_fn(|i| { + T::Packing::from(scale.as_basis_coefficients_slice()[i]) + }); + izip!(&mut acc, row).for_each(|(l, r)| *l += scale * r); + acc + }, + |mut acc_l, acc_r| { + izip!(&mut acc_l, acc_r).for_each(|(l, r)| *l += r); + acc_l + }, + ); + + packed_result + .into_iter() + .flat_map(|p| { + (0..T::Packing::WIDTH).map(move |i| { + EF::from_basis_coefficients_fn(|j| { + p.as_basis_coefficients_slice()[j].as_slice()[i] + }) + }) + }) + .take(self.width()) + .collect() + } + + /// Compute the matrix vector product `M . vec`, aka take the dot product of each + /// row of `M` by `vec`. If the length of `vec` is longer than the width of `M`, + /// `vec` is truncated to the first `width()` elements. + /// + /// We make use of `PackedFieldExtension` to speed up computations. Thus `vec` is passed in as + /// a slice of `PackedFieldExtension` elements. + /// + /// # Panics + /// This function panics if the length of `vec` is less than `self.width().div_ceil(T::Packing::WIDTH)`. + fn rowwise_packed_dot_product( + &self, + vec: &[EF::ExtensionPacking], + ) -> impl IndexedParallelIterator + where + T: Field, + EF: ExtensionField, + { + // The length of a `padded_horizontally_packed_row` is `self.width().div_ceil(T::Packing::WIDTH)`. + assert!(vec.len() >= self.width().div_ceil(T::Packing::WIDTH)); + + // TODO: This is a base - extension dot product and so it should + // be possible to speed this up using ideas in `packed_linear_combination`. + // TODO: Perhaps we should be packing rows vertically not horizontally. + self.par_padded_horizontally_packed_rows::() + .map(move |row_packed| { + let packed_sum_of_packed: EF::ExtensionPacking = + dot_product(vec.iter().copied(), row_packed); + let sum_of_packed: EF = EF::from_basis_coefficients_fn(|i| { + packed_sum_of_packed.as_basis_coefficients_slice()[i] + .as_slice() + .iter() + .copied() + .sum() + }); + sum_of_packed + }) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + use alloc::{format, vec}; + + use itertools::izip; + use p3_baby_bear::BabyBear; + use p3_field::PrimeCharacteristicRing; + use p3_field::extension::BinomialExtensionField; + use rand::SeedableRng; + use rand::rngs::SmallRng; + + use super::*; + + #[test] + fn test_columnwise_dot_product() { + type F = BabyBear; + type EF = BinomialExtensionField; + + let mut rng = SmallRng::seed_from_u64(1); + let m = RowMajorMatrix::::rand(&mut rng, 1 << 8, 1 << 4); + let v = RowMajorMatrix::::rand(&mut rng, 1 << 8, 1).values; + + let mut expected = vec![EF::ZERO; m.width()]; + for (row, &scale) in izip!(m.rows(), &v) { + for (l, r) in izip!(&mut expected, row) { + *l += scale * r; + } + } + + assert_eq!(m.columnwise_dot_product(&v), expected); + } + + // Mock implementation for testing purposes + struct MockMatrix { + data: Vec>, + width: usize, + height: usize, + } + + impl Matrix for MockMatrix { + fn width(&self) -> usize { + self.width + } + + fn height(&self) -> usize { + self.height + } + + unsafe fn row_unchecked( + &self, + r: usize, + ) -> impl IntoIterator + Send + Sync> + { + // Just a mock implementation so we just do the easy safe thing. + self.data[r].clone() + } + } + + #[test] + fn test_dimensions() { + let dims = Dimensions { + width: 3, + height: 5, + }; + assert_eq!(dims.width, 3); + assert_eq!(dims.height, 5); + assert_eq!(format!("{:?}", dims), "3x5"); + assert_eq!(format!("{}", dims), "3x5"); + } + + #[test] + fn test_mock_matrix_dimensions() { + let matrix = MockMatrix { + data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]], + width: 3, + height: 3, + }; + assert_eq!(matrix.width(), 3); + assert_eq!(matrix.height(), 3); + assert_eq!( + matrix.dimensions(), + Dimensions { + width: 3, + height: 3 + } + ); + } + + #[test] + fn test_first_row() { + let matrix = MockMatrix { + data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]], + width: 3, + height: 3, + }; + let mut first_row = matrix.first_row().unwrap().into_iter(); + assert_eq!(first_row.next(), Some(1)); + assert_eq!(first_row.next(), Some(2)); + assert_eq!(first_row.next(), Some(3)); + } + + #[test] + fn test_last_row() { + let matrix = MockMatrix { + data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]], + width: 3, + height: 3, + }; + let mut last_row = matrix.last_row().unwrap().into_iter(); + assert_eq!(last_row.next(), Some(7)); + assert_eq!(last_row.next(), Some(8)); + assert_eq!(last_row.next(), Some(9)); + } + + #[test] + fn test_first_last_row_empty_matrix() { + let matrix = MockMatrix { + data: vec![], + width: 3, + height: 0, + }; + let first_row = matrix.first_row(); + let last_row = matrix.last_row(); + assert!(first_row.is_none()); + assert!(last_row.is_none()); + } + + #[test] + fn test_to_row_major_matrix() { + let matrix = MockMatrix { + data: vec![vec![1, 2], vec![3, 4]], + width: 2, + height: 2, + }; + let row_major = matrix.to_row_major_matrix(); + assert_eq!(row_major.values, vec![1, 2, 3, 4]); + assert_eq!(row_major.width, 2); + } + + #[test] + fn test_matrix_get_methods() { + let matrix = MockMatrix { + data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]], + width: 3, + height: 3, + }; + assert_eq!(matrix.get(0, 0), Some(1)); + assert_eq!(matrix.get(1, 2), Some(6)); + assert_eq!(matrix.get(2, 1), Some(8)); + + unsafe { + assert_eq!(matrix.get_unchecked(0, 1), 2); + assert_eq!(matrix.get_unchecked(1, 0), 4); + assert_eq!(matrix.get_unchecked(2, 2), 9); + } + + assert_eq!(matrix.get(3, 0), None); // Height out of bounds + assert_eq!(matrix.get(0, 3), None); // Width out of bounds + } + + #[test] + fn test_matrix_row_methods_iteration() { + let matrix = MockMatrix { + data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]], + width: 3, + height: 3, + }; + + let mut row_iter = matrix.row(1).unwrap().into_iter(); + assert_eq!(row_iter.next(), Some(4)); + assert_eq!(row_iter.next(), Some(5)); + assert_eq!(row_iter.next(), Some(6)); + assert_eq!(row_iter.next(), None); + + unsafe { + let mut row_iter_unchecked = matrix.row_unchecked(2).into_iter(); + assert_eq!(row_iter_unchecked.next(), Some(7)); + assert_eq!(row_iter_unchecked.next(), Some(8)); + assert_eq!(row_iter_unchecked.next(), Some(9)); + assert_eq!(row_iter_unchecked.next(), None); + + let mut row_iter_subset = matrix.row_subseq_unchecked(0, 1, 3).into_iter(); + assert_eq!(row_iter_subset.next(), Some(2)); + assert_eq!(row_iter_subset.next(), Some(3)); + assert_eq!(row_iter_subset.next(), None); + } + + assert!(matrix.row(3).is_none()); // Height out of bounds + } + + #[test] + fn test_row_slice_methods() { + let matrix = MockMatrix { + data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]], + width: 3, + height: 3, + }; + let row_slice = matrix.row_slice(1).unwrap(); + assert_eq!(*row_slice, [4, 5, 6]); + unsafe { + let row_slice_unchecked = matrix.row_slice_unchecked(2); + assert_eq!(*row_slice_unchecked, [7, 8, 9]); + + let row_subslice = matrix.row_subslice_unchecked(0, 1, 2); + assert_eq!(*row_subslice, [2]); + } + + assert!(matrix.row_slice(3).is_none()); // Height out of bounds + } + + #[test] + fn test_matrix_rows() { + let matrix = MockMatrix { + data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]], + width: 3, + height: 3, + }; + + let all_rows: Vec> = matrix.rows().map(|row| row.collect()).collect(); + assert_eq!(all_rows, vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/row_index_mapped.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/row_index_mapped.rs new file mode 100644 index 00000000..44476b13 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/row_index_mapped.rs @@ -0,0 +1,446 @@ +use core::ops::Deref; + +use p3_field::PackedValue; + +use crate::Matrix; +use crate::dense::RowMajorMatrix; + +/// A trait for remapping row indices of a matrix. +/// +/// Implementations can change the number of visible rows (`height`) +/// and define how a given logical row index maps to a physical one. +pub trait RowIndexMap: Send + Sync { + /// Returns the number of rows exposed by the mapping. + fn height(&self) -> usize; + + /// Maps a visible row index `r` to the corresponding row index in the underlying matrix. + /// + /// The input `r` is assumed to lie in the range `0..self.height()` and the output + /// will lie in the range `0..self.inner.height()`. + /// + /// It is considered undefined behaviour to call `map_row_index` with `r >= self.height()`. + fn map_row_index(&self, r: usize) -> usize; + + /// Converts the mapped matrix into a dense row-major matrix. + /// + /// This default implementation iterates over all mapped rows, + /// collects them in order, and builds a dense representation. + fn to_row_major_matrix>( + &self, + inner: Inner, + ) -> RowMajorMatrix { + RowMajorMatrix::new( + unsafe { + // Safety: The output of `map_row_index` is less than `inner.height()` for all inputs in the range `0..self.height()`. + (0..self.height()) + .flat_map(|r| inner.row_unchecked(self.map_row_index(r))) + .collect() + }, + inner.width(), + ) + } +} + +/// A matrix view that applies a row index mapping to an inner matrix. +/// +/// The mapping changes which rows are visible and in what order. +/// The width remains unchanged. +#[derive(Copy, Clone, Debug)] +pub struct RowIndexMappedView { + /// A row index mapping that defines the number and order of visible rows. + pub index_map: IndexMap, + /// The inner matrix that holds actual data. + pub inner: Inner, +} + +impl> Matrix + for RowIndexMappedView +{ + fn width(&self) -> usize { + self.inner.width() + } + + fn height(&self) -> usize { + self.index_map.height() + } + + unsafe fn get_unchecked(&self, r: usize, c: usize) -> T { + unsafe { + // Safety: The caller must ensure that r < self.height() and c < self.width(). + self.inner.get_unchecked(self.index_map.map_row_index(r), c) + } + } + + unsafe fn row_unchecked( + &self, + r: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height(). + self.inner.row_unchecked(self.index_map.map_row_index(r)) + } + } + + unsafe fn row_subseq_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height() and start <= end <= self.width(). + self.inner + .row_subseq_unchecked(self.index_map.map_row_index(r), start, end) + } + } + + unsafe fn row_slice_unchecked(&self, r: usize) -> impl Deref { + unsafe { + // Safety: The caller must ensure that r < self.height(). + self.inner + .row_slice_unchecked(self.index_map.map_row_index(r)) + } + } + + unsafe fn row_subslice_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl Deref { + unsafe { + // Safety: The caller must ensure that r < self.height() and start <= end <= self.width(). + self.inner + .row_subslice_unchecked(self.index_map.map_row_index(r), start, end) + } + } + + fn to_row_major_matrix(self) -> RowMajorMatrix + where + Self: Sized, + T: Clone, + { + // Use Perm's optimized permutation routine, if it has one. + self.index_map.to_row_major_matrix(self.inner) + } + + fn horizontally_packed_row<'a, P>( + &'a self, + r: usize, + ) -> ( + impl Iterator + Send + Sync, + impl Iterator + Send + Sync, + ) + where + P: PackedValue, + T: Clone + 'a, + { + self.inner + .horizontally_packed_row(self.index_map.map_row_index(r)) + } + + fn padded_horizontally_packed_row<'a, P>( + &'a self, + r: usize, + ) -> impl Iterator + Send + Sync + where + P: PackedValue, + T: Clone + Default + 'a, + { + self.inner + .padded_horizontally_packed_row(self.index_map.map_row_index(r)) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + use alloc::vec::Vec; + + use itertools::Itertools; + use p3_baby_bear::BabyBear; + use p3_field::FieldArray; + + use super::*; + use crate::dense::RowMajorMatrix; + + /// Mock implementation of RowIndexMap + struct IdentityMap(usize); + + impl RowIndexMap for IdentityMap { + fn height(&self) -> usize { + self.0 + } + + fn map_row_index(&self, r: usize) -> usize { + r + } + } + + /// Another mock implementation for reversing rows + struct ReverseMap(usize); + + impl RowIndexMap for ReverseMap { + fn height(&self) -> usize { + self.0 + } + + fn map_row_index(&self, r: usize) -> usize { + self.0 - 1 - r + } + } + + /// A final Mock implementation of RowIndexMap + struct ConstantMap; + + impl RowIndexMap for ConstantMap { + fn height(&self) -> usize { + 1 + } + + fn map_row_index(&self, _r: usize) -> usize { + 0 + } + } + + #[test] + fn test_identity_row_index_map() { + // Create an inner matrix. + // The matrix will be: + // [ 1 2 3 ] + // [ 4 5 6 ] + let inner = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 3); + + // Create a mapped view using an `IdentityMap`, which does not alter row indices. + let mapped_view = RowIndexMappedView { + index_map: IdentityMap(inner.height()), + inner, + }; + + // Check dimensions. + assert_eq!(mapped_view.height(), 2); + assert_eq!(mapped_view.width(), 3); + + // Check values. + assert_eq!(mapped_view.get(0, 0).unwrap(), 1); + assert_eq!(mapped_view.get(1, 2).unwrap(), 6); + + unsafe { + assert_eq!(mapped_view.get_unchecked(0, 1), 2); + assert_eq!(mapped_view.get_unchecked(1, 0), 4); + } + + // Check rows. + let rows: Vec> = mapped_view.rows().map(|row| row.collect()).collect(); + assert_eq!(rows, vec![vec![1, 2, 3], vec![4, 5, 6]]); + + // Check dense matrix. + let dense = mapped_view.to_row_major_matrix(); + assert_eq!(dense.values, vec![1, 2, 3, 4, 5, 6]); + } + + #[test] + fn test_reverse_row_index_map() { + // Create an inner matrix. + // The matrix will be: + // [ 1 2 3 ] + // [ 4 5 6 ] + let inner = RowMajorMatrix::new(vec![1, 2, 3, 4, 5, 6], 3); + + // Create a mapped view using a ReverseMap, which reverses row indices. + let mapped_view = RowIndexMappedView { + index_map: ReverseMap(inner.height()), + inner, + }; + + // Check dimensions. + assert_eq!(mapped_view.height(), 2); + assert_eq!(mapped_view.width(), 3); + + // Check the first element of the mapped view (originally the second row, first column). + assert_eq!(mapped_view.get(0, 0).unwrap(), 4); + // Check the last element of the mapped view (originally the first row, last column). + assert_eq!(mapped_view.get(1, 2).unwrap(), 3); + + unsafe { + assert_eq!(mapped_view.get_unchecked(0, 1), 5); + assert_eq!(mapped_view.get_unchecked(1, 0), 1); + } + + // Check rows. + let rows: Vec> = mapped_view.rows().map(|row| row.collect()).collect(); + assert_eq!(rows, vec![vec![4, 5, 6], vec![1, 2, 3]]); + + // Check dense matrix. + let dense = mapped_view.to_row_major_matrix(); + assert_eq!(dense.values, vec![4, 5, 6, 1, 2, 3]); + } + + #[test] + fn test_horizontally_packed_row() { + // Define the packed type with width 2 + type Packed = FieldArray; + + // Create an inner matrix of BabyBear elements. + // Matrix layout: + // [ 1 2 ] + // [ 3 4 ] + let inner = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + ], + 2, + ); + + // Apply a reverse row index mapping. + let mapped_view = RowIndexMappedView { + index_map: ReverseMap(inner.height()), + inner, + }; + + // Extract the packed and suffix iterators from row 0 (which is reversed row 1). + let (packed_iter, suffix_iter) = mapped_view.horizontally_packed_row::(0); + + // Collect iterators to concrete values. + let packed: Vec<_> = packed_iter.collect(); + let suffix: Vec<_> = suffix_iter.collect(); + + // Check the packed row values match reversed second row. + assert_eq!( + packed, + &[Packed::from([BabyBear::new(3), BabyBear::new(4)])] + ); + + // Check there are no suffix leftovers. + assert!(suffix.is_empty()); + } + + #[test] + fn test_padded_horizontally_packed_row() { + // Define a packed type with width 3 + type Packed = FieldArray; + + // Create a 2x2 matrix of BabyBear elements: + // [ 1 2 ] + // [ 3 4 ] + let inner = RowMajorMatrix::new( + vec![ + BabyBear::new(1), + BabyBear::new(2), + BabyBear::new(3), + BabyBear::new(4), + ], + 2, + ); + + // Use identity mapping (rows remain unchanged). + let mapped_view = RowIndexMappedView { + index_map: IdentityMap(inner.height()), + inner, + }; + + // Pad the second row (row 1) into chunks of size 3. + let packed: Vec<_> = mapped_view + .padded_horizontally_packed_row::(1) + .collect(); + + // Verify the packed result includes padding with zero at the end. + assert_eq!( + packed, + vec![Packed::from([ + BabyBear::new(3), + BabyBear::new(4), + BabyBear::new(0), + ])] + ); + } + + #[test] + fn test_row_and_row_slice_methods() { + // Create a 2x3 matrix of integers: + // [ 10 20 30 ] + // [ 40 50 60 ] + let inner = RowMajorMatrix::new(vec![10, 20, 30, 40, 50, 60], 3); + + // Apply reverse row mapping (row 0 becomes 1, row 1 becomes 0). + let mapped_view = RowIndexMappedView { + index_map: ReverseMap(inner.height()), + inner, + }; + + // Get row slices through dereferencing and verify content. + assert_eq!(mapped_view.row_slice(0).unwrap().deref(), &[40, 50, 60]); // was row 1 + assert_eq!( + mapped_view.row(1).unwrap().into_iter().collect_vec(), + vec![10, 20, 30] + ); // was row 0 + + unsafe { + // Check unsafe row slices. + assert_eq!( + mapped_view.row_unchecked(0).into_iter().collect_vec(), + vec![40, 50, 60] + ); // was row 1 + assert_eq!(mapped_view.row_slice_unchecked(1).deref(), &[10, 20, 30]); // was row 0 + + assert_eq!( + mapped_view.row_subslice_unchecked(0, 1, 3).deref(), + &[50, 60] + ); // was row 1 + assert_eq!( + mapped_view + .row_subseq_unchecked(1, 0, 2) + .into_iter() + .collect_vec(), + vec![10, 20] + ); // was row 0 + } + + assert!(mapped_view.row(2).is_none()); // Height out of bounds. + assert!(mapped_view.row_slice(2).is_none()); // Height out of bounds. + } + + #[test] + fn test_out_of_bounds_access() { + // Create a 2x2 matrix: + // [ 1 2 ] + // [ 3 4 ] + let inner = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); + + // Use identity mapping. + let mapped_view = RowIndexMappedView { + index_map: IdentityMap(inner.height()), + inner, + }; + + // Attempt to access out-of-bounds row (index 2). Should panic. + assert_eq!(mapped_view.get(2, 1), None); + assert!(mapped_view.row(5).is_none()); + assert!(mapped_view.row_slice(11).is_none()); + assert_eq!(mapped_view.get(0, 20), None); + } + + #[test] + fn test_out_of_bounds_access_with_bad_map() { + // Create a 2x2 matrix: + // [ 1 2 ] + // [ 3 4 ] + let inner = RowMajorMatrix::new(vec![1, 2, 3, 4], 4); + + // Use identity mapping. + let mapped_view = RowIndexMappedView { + index_map: ConstantMap, + inner, + }; + + assert_eq!(mapped_view.get(0, 2), Some(3)); + + // Attempt to access out-of-bounds row (index 1). Should panic. + assert_eq!(mapped_view.get(1, 0), None); + assert!(mapped_view.row(1).is_none()); + assert!(mapped_view.row_slice(1).is_none()); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/stack.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/stack.rs new file mode 100644 index 00000000..b15843f1 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/stack.rs @@ -0,0 +1,383 @@ +use core::ops::Deref; + +use crate::Matrix; + +/// A matrix composed by stacking two matrices vertically, one on top of the other. +/// +/// Both matrices must have the same `width`. +/// The resulting matrix has dimensions: +/// - `width`: The same as the inputs. +/// - `height`: The sum of the `heights` of the input matrices. +/// +/// Element access and iteration will first access the rows of the top matrix, +/// followed by the rows of the bottom matrix. +#[derive(Copy, Clone, Debug)] +pub struct VerticalPair { + /// The top matrix in the vertical composition. + pub top: Top, + /// The bottom matrix in the vertical composition. + pub bottom: Bottom, +} + +/// A matrix composed by placing two matrices side-by-side horizontally. +/// +/// Both matrices must have the same `height`. +/// The resulting matrix has dimensions: +/// - `width`: The sum of the `widths` of the input matrices. +/// - `height`: The same as the inputs. +/// +/// Element access and iteration for a given row `i` will first access the elements in the `i`'th row of the left matrix, +/// followed by elements in the `i'`th row of the right matrix. +#[derive(Copy, Clone, Debug)] +pub struct HorizontalPair { + /// The left matrix in the horizontal composition. + pub left: Left, + /// The right matrix in the horizontal composition. + pub right: Right, +} + +impl VerticalPair { + /// Create a new `VerticalPair` by stacking two matrices vertically. + /// + /// # Panics + /// Panics if the two matrices do not have the same width (i.e., number of columns), + /// since vertical composition requires column alignment. + /// + /// # Returns + /// A `VerticalPair` that represents the combined matrix. + pub fn new(top: Top, bottom: Bottom) -> Self + where + T: Send + Sync + Clone, + Top: Matrix, + Bottom: Matrix, + { + assert_eq!(top.width(), bottom.width()); + Self { top, bottom } + } +} + +impl HorizontalPair { + /// Create a new `HorizontalPair` by joining two matrices side by side. + /// + /// # Panics + /// Panics if the two matrices do not have the same height (i.e., number of rows), + /// since horizontal composition requires row alignment. + /// + /// # Returns + /// A `HorizontalPair` that represents the combined matrix. + pub fn new(left: Left, right: Right) -> Self + where + T: Send + Sync + Clone, + Left: Matrix, + Right: Matrix, + { + assert_eq!(left.height(), right.height()); + Self { left, right } + } +} + +impl, Bottom: Matrix> Matrix + for VerticalPair +{ + fn width(&self) -> usize { + self.top.width() + } + + fn height(&self) -> usize { + self.top.height() + self.bottom.height() + } + + unsafe fn get_unchecked(&self, r: usize, c: usize) -> T { + unsafe { + // Safety: The caller must ensure that r < self.height() and c < self.width() + if r < self.top.height() { + self.top.get_unchecked(r, c) + } else { + self.bottom.get_unchecked(r - self.top.height(), c) + } + } + } + + unsafe fn row_unchecked( + &self, + r: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height() + if r < self.top.height() { + EitherRow::Left(self.top.row_unchecked(r).into_iter()) + } else { + EitherRow::Right(self.bottom.row_unchecked(r - self.top.height()).into_iter()) + } + } + } + + unsafe fn row_subseq_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height() and start <= end <= self.width() + if r < self.top.height() { + EitherRow::Left(self.top.row_subseq_unchecked(r, start, end).into_iter()) + } else { + EitherRow::Right( + self.bottom + .row_subseq_unchecked(r - self.top.height(), start, end) + .into_iter(), + ) + } + } + } + + unsafe fn row_slice_unchecked(&self, r: usize) -> impl Deref { + unsafe { + // Safety: The caller must ensure that r < self.height() + if r < self.top.height() { + EitherRow::Left(self.top.row_slice_unchecked(r)) + } else { + EitherRow::Right(self.bottom.row_slice_unchecked(r - self.top.height())) + } + } + } + + unsafe fn row_subslice_unchecked( + &self, + r: usize, + start: usize, + end: usize, + ) -> impl Deref { + unsafe { + // Safety: The caller must ensure that r < self.height() and start <= end <= self.width() + if r < self.top.height() { + EitherRow::Left(self.top.row_subslice_unchecked(r, start, end)) + } else { + EitherRow::Right(self.bottom.row_subslice_unchecked( + r - self.top.height(), + start, + end, + )) + } + } + } +} + +impl, Right: Matrix> Matrix + for HorizontalPair +{ + fn width(&self) -> usize { + self.left.width() + self.right.width() + } + + fn height(&self) -> usize { + self.left.height() + } + + unsafe fn get_unchecked(&self, r: usize, c: usize) -> T { + unsafe { + // Safety: The caller must ensure that r < self.height() and c < self.width() + if c < self.left.width() { + self.left.get_unchecked(r, c) + } else { + self.right.get_unchecked(r, c - self.left.width()) + } + } + } + + unsafe fn row_unchecked( + &self, + r: usize, + ) -> impl IntoIterator + Send + Sync> { + unsafe { + // Safety: The caller must ensure that r < self.height() + self.left + .row_unchecked(r) + .into_iter() + .chain(self.right.row_unchecked(r)) + } + } +} + +/// We use this to wrap both the row iterator and the row slice. +#[derive(Debug)] +pub enum EitherRow { + Left(L), + Right(R), +} + +impl Iterator for EitherRow +where + L: Iterator, + R: Iterator, +{ + type Item = T; + + fn next(&mut self) -> Option { + match self { + Self::Left(l) => l.next(), + Self::Right(r) => r.next(), + } + } +} + +impl Deref for EitherRow +where + L: Deref, + R: Deref, +{ + type Target = [T]; + fn deref(&self) -> &Self::Target { + match self { + Self::Left(l) => l, + Self::Right(r) => r, + } + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + use alloc::vec::Vec; + + use itertools::Itertools; + + use super::*; + use crate::RowMajorMatrix; + + #[test] + fn test_vertical_pair_empty_top() { + let top = RowMajorMatrix::new(vec![], 2); // 0x2 + let bottom = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); // 2x2 + let vpair = VerticalPair::new::(top, bottom); + assert_eq!(vpair.height(), 2); + assert_eq!(vpair.get(1, 1), Some(4)); + unsafe { + assert_eq!(vpair.get_unchecked(0, 0), 1); + } + } + + #[test] + fn test_vertical_pair_composition() { + let top = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); // 2x2 + let bottom = RowMajorMatrix::new(vec![5, 6, 7, 8], 2); // 2x2 + let vertical = VerticalPair::new::(top, bottom); + + // Dimensions + assert_eq!(vertical.width(), 2); + assert_eq!(vertical.height(), 4); + + // Values from top + assert_eq!(vertical.get(0, 0), Some(1)); + assert_eq!(vertical.get(1, 1), Some(4)); + + // Values from bottom + unsafe { + assert_eq!(vertical.get_unchecked(2, 0), 5); + assert_eq!(vertical.get_unchecked(3, 1), 8); + } + + // Row iter from bottom + let row = vertical.row(3).unwrap().into_iter().collect_vec(); + assert_eq!(row, vec![7, 8]); + + unsafe { + // Row iter from top + let row = vertical.row_unchecked(1).into_iter().collect_vec(); + assert_eq!(row, vec![3, 4]); + + let row = vertical + .row_subseq_unchecked(0, 0, 1) + .into_iter() + .collect_vec(); + assert_eq!(row, vec![1]); + } + + // Row slice + assert_eq!(vertical.row_slice(2).unwrap().deref(), &[5, 6]); + + unsafe { + // Row slice unchecked + assert_eq!(vertical.row_slice_unchecked(3).deref(), &[7, 8]); + assert_eq!(vertical.row_subslice_unchecked(1, 1, 2).deref(), &[4]); + } + + assert_eq!(vertical.get(0, 2), None); // Width out of bounds + assert_eq!(vertical.get(4, 0), None); // Height out of bounds + assert!(vertical.row(4).is_none()); // Height out of bounds + assert!(vertical.row_slice(4).is_none()); // Height out of bounds + } + + #[test] + fn test_horizontal_pair_composition() { + let left = RowMajorMatrix::new(vec![1, 2, 3, 4], 2); // 2x2 + let right = RowMajorMatrix::new(vec![5, 6, 7, 8], 2); // 2x2 + let horizontal = HorizontalPair::new::(left, right); + + // Dimensions + assert_eq!(horizontal.height(), 2); + assert_eq!(horizontal.width(), 4); + + // Left values + assert_eq!(horizontal.get(0, 0), Some(1)); + assert_eq!(horizontal.get(1, 1), Some(4)); + + // Right values + unsafe { + assert_eq!(horizontal.get_unchecked(0, 2), 5); + assert_eq!(horizontal.get_unchecked(1, 3), 8); + } + + // Row iter + let row = horizontal.row(0).unwrap().into_iter().collect_vec(); + assert_eq!(row, vec![1, 2, 5, 6]); + + unsafe { + let row = horizontal.row_unchecked(1).into_iter().collect_vec(); + assert_eq!(row, vec![3, 4, 7, 8]); + } + + assert_eq!(horizontal.get(0, 4), None); // Width out of bounds + assert_eq!(horizontal.get(2, 0), None); // Height out of bounds + assert!(horizontal.row(2).is_none()); // Height out of bounds + } + + #[test] + fn test_either_row_iterator_behavior() { + type Iter = alloc::vec::IntoIter; + + // Left variant + let left: EitherRow = EitherRow::Left(vec![10, 20].into_iter()); + assert_eq!(left.collect::>(), vec![10, 20]); + + // Right variant + let right: EitherRow = EitherRow::Right(vec![30, 40].into_iter()); + assert_eq!(right.collect::>(), vec![30, 40]); + } + + #[test] + fn test_either_row_deref_behavior() { + let left: EitherRow<&[i32], &[i32]> = EitherRow::Left(&[1, 2, 3]); + let right: EitherRow<&[i32], &[i32]> = EitherRow::Right(&[4, 5]); + + assert_eq!(&*left, &[1, 2, 3]); + assert_eq!(&*right, &[4, 5]); + } + + #[test] + #[should_panic] + fn test_vertical_pair_width_mismatch_should_panic() { + let a = RowMajorMatrix::new(vec![1, 2, 3], 1); // 3x1 + let b = RowMajorMatrix::new(vec![4, 5], 2); // 1x2 + let _ = VerticalPair::new::(a, b); + } + + #[test] + #[should_panic] + fn test_horizontal_pair_height_mismatch_should_panic() { + let a = RowMajorMatrix::new(vec![1, 2, 3], 3); // 1x3 + let b = RowMajorMatrix::new(vec![4, 5], 1); // 2x1 + let _ = HorizontalPair::new::(a, b); + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/strided.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/strided.rs new file mode 100644 index 00000000..6944aa03 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/strided.rs @@ -0,0 +1,183 @@ +use crate::Matrix; +use crate::row_index_mapped::{RowIndexMap, RowIndexMappedView}; + +/// A vertical row-mapping strategy that selects every `stride`-th row from an inner matrix, +/// starting at a fixed `offset`. +/// +/// This enables vertical striding like selecting rows: `offset`, `offset + stride`, etc. +#[derive(Debug)] +pub struct VerticallyStridedRowIndexMap { + /// The number of rows in the resulting view. + height: usize, + /// The step size between selected rows in the inner matrix. + stride: usize, + /// The offset to start the stride from. + offset: usize, +} + +pub type VerticallyStridedMatrixView = + RowIndexMappedView; + +impl VerticallyStridedRowIndexMap { + /// Create a new vertically strided view over a matrix. + /// + /// This selects rows in the inner matrix starting from `offset`, and then every `stride` rows after. + /// + /// # Arguments + /// - `inner`: The inner matrix to view. + /// - `stride`: The number of rows between each selected row. + /// - `offset`: The initial row to start from. + pub fn new_view>( + inner: Inner, + stride: usize, + offset: usize, + ) -> VerticallyStridedMatrixView { + let h = inner.height(); + let full_strides = h / stride; + let remainder = h % stride; + let final_stride = offset < remainder; + let height = full_strides + final_stride as usize; + RowIndexMappedView { + index_map: Self { + height, + stride, + offset, + }, + inner, + } + } +} + +impl RowIndexMap for VerticallyStridedRowIndexMap { + fn height(&self) -> usize { + self.height + } + + fn map_row_index(&self, r: usize) -> usize { + r * self.stride + self.offset + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use super::*; + use crate::{Matrix, RowMajorMatrix}; + + fn sample_matrix() -> RowMajorMatrix { + // A 5x3 matrix: + // [10, 11, 12] + // [20, 21, 22] + // [30, 31, 32] + // [40, 41, 42] + // [50, 51, 52] + RowMajorMatrix::new( + vec![10, 11, 12, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52], + 3, + ) + } + + #[test] + fn test_vertically_strided_view_stride_1_offset_0() { + let matrix = sample_matrix(); + let view = VerticallyStridedRowIndexMap::new_view(matrix, 1, 0); + + assert_eq!(view.height(), 5); + assert_eq!(view.width(), 3); + + assert_eq!(view.get(0, 0), Some(10)); + assert_eq!(view.get(1, 1), Some(21)); + unsafe { + assert_eq!(view.get_unchecked(4, 2), 52); + } + assert_eq!(view.get(5, 0), None); // out of bounds + assert_eq!(view.get(0, 3), None); // out of bounds + } + + #[test] + fn test_vertically_strided_view_stride_2_offset_0() { + let matrix = sample_matrix(); + let view = VerticallyStridedRowIndexMap::new_view(matrix, 2, 0); + + assert_eq!(view.height(), 3); + assert_eq!(view.get(0, 0), Some(10)); // row 0 + unsafe { + assert_eq!(view.get_unchecked(1, 1), 31); // row 2 + assert_eq!(view.get_unchecked(2, 2), 52); // row 4 + } + assert_eq!(view.get(0, 3), None); // out of bounds + } + + #[test] + fn test_vertically_strided_view_stride_2_offset_1() { + let matrix = sample_matrix(); + let view = VerticallyStridedRowIndexMap::new_view(matrix, 2, 1); + + assert_eq!(view.height(), 2); + assert_eq!(view.get(0, 0), Some(20)); // row 1 + unsafe { + assert_eq!(view.get_unchecked(1, 1), 41); + } // row 3 + } + + #[test] + fn test_vertically_strided_view_stride_3_offset_0() { + let matrix = sample_matrix(); + let view = VerticallyStridedRowIndexMap::new_view(matrix, 3, 0); + + assert_eq!(view.height(), 2); + assert_eq!(view.get(0, 0), Some(10)); // row 0 + assert_eq!(view.get(1, 1), Some(41)); // row 3 + } + + #[test] + fn test_vertically_strided_view_stride_3_offset_1() { + let matrix = sample_matrix(); + let view = VerticallyStridedRowIndexMap::new_view(matrix, 3, 1); + + assert_eq!(view.height(), 2); + unsafe { + assert_eq!(view.get_unchecked(0, 0), 20); // row 1 + assert_eq!(view.get_unchecked(1, 1), 51); // row 4 + } + } + + #[test] + fn test_vertically_strided_view_stride_3_offset_2() { + let matrix = sample_matrix(); + let view = VerticallyStridedRowIndexMap::new_view(matrix, 3, 2); + + assert_eq!(view.height(), 1); + assert_eq!(view.get(0, 2), Some(32)); // row 2 + } + + #[test] + fn test_vertically_strided_view_stride_greater_than_height() { + let matrix = sample_matrix(); + let view = VerticallyStridedRowIndexMap::new_view(matrix, 10, 0); + + assert_eq!(view.height(), 1); + assert_eq!(view.get(0, 0), Some(10)); // row 0 + } + + #[test] + fn test_vertically_strided_view_stride_greater_than_height_with_valid_offset() { + let matrix = sample_matrix(); // height = 5 + let view = VerticallyStridedRowIndexMap::new_view(matrix, 10, 4); + + // offset == 4 < height == 5 → view selects row 4 + assert_eq!(view.height(), 1); + assert_eq!(view.get(0, 2), Some(52)); // row 4 + } + + #[test] + fn test_vertically_strided_view_stride_greater_than_height_with_offset_beyond_height() { + let matrix = sample_matrix(); // height = 5 + let view = VerticallyStridedRowIndexMap::new_view(matrix, 10, 6); + + // offset == 6 > height == 5 → no valid row + assert_eq!(view.height(), 0); + assert_eq!(view.get(0, 0), None); // out of bounds + } +} diff --git a/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/util.rs b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/util.rs new file mode 100644 index 00000000..79170651 --- /dev/null +++ b/crates/neo-fold/riscv-tests/guests/circuit-l2-transfer/vendor/p3-matrix/src/util.rs @@ -0,0 +1,221 @@ +use core::borrow::BorrowMut; + +use p3_maybe_rayon::prelude::*; +use p3_util::{log2_strict_usize, reverse_bits_len}; + +use crate::Matrix; +use crate::dense::{DenseMatrix, DenseStorage, RowMajorMatrix}; + +/// Reverse the order of matrix rows based on the bit-reversal of their indices. +/// +/// Given a matrix `mat` of height `h = 2^k`, this function rearranges its rows by +/// reversing the binary representation of each row index. For example, if `h = 8` (i.e., 3 bits): +/// +/// ```text +/// Original Index Binary Reversed Target Index +/// -------------- ------- --------- ------------- +/// 0 000 000 0 +/// 1 001 100 4 +/// 2 010 010 2 +/// 3 011 110 6 +/// 4 100 001 1 +/// 5 101 101 5 +/// 6 110 011 3 +/// 7 111 111 7 +/// ``` +/// +/// The transformation is performed in-place. +/// +/// # Panics +/// Panics if the height of the matrix is not a power of two. +/// +/// # Arguments +/// - `mat`: The matrix whose rows should be reordered. +pub fn reverse_matrix_index_bits<'a, F, S>(mat: &mut DenseMatrix) +where + F: Clone + Send + Sync + 'a, + S: DenseStorage + BorrowMut<[F]>, +{ + let w = mat.width(); + let h = mat.height(); + let log_h = log2_strict_usize(h); + let values = mat.values.borrow_mut().as_mut_ptr() as usize; + + // SAFETY: Due to the i < j check, we are guaranteed that `swap_rows_raw + // will never try and access a particular slice of data more than once + // across all parallel threads. Hence the following code is safe and does + // not trigger undefined behaviour. + (0..h).into_par_iter().for_each(|i| { + let values = values as *mut F; + let j = reverse_bits_len(i, log_h); + if i < j { + unsafe { swap_rows_raw(values, w, i, j) }; + } + }); +} + +/// Swap two rows `i` and `j` in a [`RowMajorMatrix`]. +/// +/// # Panics +/// Panics if the indices are out of bounds or not ordered as `i < j`. +/// +/// # Arguments +/// - `mat`: The matrix to modify. +/// - `i`: The first row index (must be less than `j`). +/// - `j`: The second row index. +pub fn swap_rows(mat: &mut RowMajorMatrix, i: usize, j: usize) { + let w = mat.width(); + let (upper, lower) = mat.values.split_at_mut(j * w); + let row_i = &mut upper[i * w..(i + 1) * w]; + let row_j = &mut lower[..w]; + row_i.swap_with_slice(row_j); +} + +/// Swap two rows `i` and `j` in-place using raw pointer access. +/// +/// This function is equivalent to [`swap_rows`] but uses unsafe raw pointer math for better performance. +/// +/// # Safety +/// - The caller must ensure `i < j < h`, where `h` is the height of the matrix. +/// - The pointer must point to a vector corresponding to a matrix of width `w`. +/// +/// # Arguments +/// - `mat`: A mutable pointer to the underlying matrix data. +/// - `w`: The matrix width (number of columns). +/// - `i`: The first row index. +/// - `j`: The second row index. +unsafe fn swap_rows_raw(mat: *mut F, w: usize, i: usize, j: usize) { + unsafe { + let row_i = core::slice::from_raw_parts_mut(mat.add(i * w), w); + let row_j = core::slice::from_raw_parts_mut(mat.add(j * w), w); + row_i.swap_with_slice(row_j); + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use super::*; + use crate::dense::RowMajorMatrix; + + #[test] + fn test_swap_rows_basic() { + let mut matrix = RowMajorMatrix::new( + vec![ + 1, 2, 3, // row 0 + 4, 5, 6, // row 1 + 7, 8, 9, // row 2 + 10, 11, 12, // row 3 + ], + 3, + ); + + // Swap rows 0 and 2 + swap_rows(&mut matrix, 0, 2); + + assert_eq!( + matrix.values, + vec![ + 7, 8, 9, // row 0 (was row 2) + 4, 5, 6, // row 1 (unchanged) + 1, 2, 3, // row 2 (was row 0) + 10, 11, 12, // row 3 (unchanged) + ] + ); + } + + #[test] + fn test_swap_rows_raw_basic() { + let mut matrix = RowMajorMatrix::new( + vec![ + 1, 2, 3, // row 0 + 4, 5, 6, // row 1 + 7, 8, 9, // row 2 + ], + 3, + ); + let ptr = matrix.values.as_mut_ptr(); + unsafe { + swap_rows_raw(ptr, matrix.width(), 0, 2); + } + + assert_eq!( + matrix.values, + vec![ + 7, 8, 9, // row 0 (was row 2) + 4, 5, 6, // row 1 (unchanged) + 1, 2, 3, // row 2 (was row 0) + ] + ); + } + + #[test] + fn test_reverse_matrix_index_bits_pow2_height() { + let mut matrix = RowMajorMatrix::new( + vec![ + 0, 1, // row 0 + 2, 3, // row 1 + 4, 5, // row 2 + 6, 7, // row 3 + 8, 9, // row 4 + 10, 11, // row 5 + 12, 13, // row 6 + 14, 15, // row 7 + ], + 2, + ); + + reverse_matrix_index_bits(&mut matrix); + + assert_eq!( + matrix.values, + vec![ + 0, 1, // row 0 → index 0b000 → stays at 0 + 8, 9, // row 1 → index 0b001 → goes to index 4 + 4, 5, // row 2 → index 0b010 → stays + 12, 13, // row 3 → index 0b011 → goes to index 6 + 2, 3, // row 4 → index 0b100 → was row 1 + 10, 11, // row 5 → index 0b101 → stays + 6, 7, // row 6 → index 0b110 → was row 3 + 14, 15, // row 7 → index 0b111 → stays + ] + ); + } + + #[test] + fn test_reverse_matrix_index_bits_height_1() { + let mut matrix = RowMajorMatrix::new( + vec![ + 42, 43, // row 0 + ], + 2, + ); + + // Bit-reversing a height-1 matrix should do nothing. + reverse_matrix_index_bits(&mut matrix); + + assert_eq!( + matrix.values, + vec![ + 42, 43, // row 0 (unchanged) + ] + ); + } + + #[test] + #[should_panic] + fn test_reverse_matrix_index_bits_non_power_of_two_should_panic() { + // height = 3 → not a power of two → should panic + let mut matrix = RowMajorMatrix::new( + vec![ + 1, 2, // row 0 + 3, 4, // row 1 + 5, 6, // row 2 + ], + 2, + ); + + reverse_matrix_index_bits(&mut matrix); + } +} diff --git a/crates/neo-fold/riscv-tests/test_riscv_circuit_l2_transfer_compiled_trace_prove_verify.rs b/crates/neo-fold/riscv-tests/test_riscv_circuit_l2_transfer_compiled_trace_prove_verify.rs new file mode 100644 index 00000000..b0a14526 --- /dev/null +++ b/crates/neo-fold/riscv-tests/test_riscv_circuit_l2_transfer_compiled_trace_prove_verify.rs @@ -0,0 +1,206 @@ +//! Compiled ROM coverage for the circuit_l2_transfer guest. +//! +//! Runtime prove/verify currently exercises the missing Poseidon2 precompile path. + +#[path = "binaries/circuit_l2_transfer_rom.rs"] +mod circuit_l2_transfer_rom; + +use neo_fold::riscv_trace_shard::Rv32TraceWiring; +use neo_memory::riscv::exec_table::Rv32ExecTable; +use neo_memory::riscv::lookups::{decode_program, RiscvCpu, RiscvMemory, RiscvShoutTables, PROG_ID}; +use neo_vm_trace::{trace_program, TwistOpKind}; +use std::time::Instant; + +fn parse_row_idx(err: &str) -> Option { + let marker = "row="; + let start = err.find(marker)? + marker.len(); + let tail = &err[start..]; + let end = tail.find(',').unwrap_or(tail.len()); + tail[..end].trim().parse::().ok() +} + +fn dump_exec_row_context(program_base: u64, program_bytes: &[u8], max_steps: usize, center_row: usize) { + let decoded_program = match decode_program(program_bytes) { + Ok(p) => p, + Err(e) => { + println!("debug_trace_error=decode_program failed: {e}"); + return; + } + }; + let mut cpu = RiscvCpu::new(32); + cpu.load_program(program_base, decoded_program); + let twist = RiscvMemory::with_program_in_twist(32, PROG_ID, program_base, program_bytes); + let shout = RiscvShoutTables::new(32); + let trace = match trace_program(cpu, twist, shout, max_steps) { + Ok(t) => t, + Err(e) => { + println!("debug_trace_error=trace_program failed: {e}"); + return; + } + }; + let exec = match Rv32ExecTable::from_trace_padded(&trace, trace.steps.len()) { + Ok(e) => e, + Err(e) => { + println!("debug_trace_error=Rv32ExecTable::from_trace_padded failed: {e}"); + return; + } + }; + + let start = center_row.saturating_sub(2); + let end = (center_row + 3).min(exec.rows.len()); + println!("debug_exec_rows_window=[{start}..{end}) total_rows={}", exec.rows.len()); + for i in start..end { + let row = &exec.rows[i]; + let rs1 = row.reg_read_lane0.as_ref().map(|v| (v.addr, v.value)); + let rs2 = row.reg_read_lane1.as_ref().map(|v| (v.addr, v.value)); + let rdw = row.reg_write_lane0.as_ref().map(|v| (v.addr, v.value)); + println!( + "debug_row idx={i} cycle={} pc_before={:#x} pc_after={:#x} instr_word={:#010x} decoded={:?}", + row.cycle, row.pc_before, row.pc_after, row.instr_word, row.decoded + ); + println!( + "debug_row_io idx={i} active={} halted={} rs1={:?} rs2={:?} rd_write={:?}", + row.active, row.halted, rs1, rs2, rdw + ); + if row.ram_events.is_empty() { + println!("debug_row_ram idx={i} ram_events=[]"); + } else { + for (eidx, ev) in row.ram_events.iter().enumerate() { + let kind = match ev.kind { + TwistOpKind::Read => "read", + TwistOpKind::Write => "write", + }; + println!( + "debug_row_ram idx={i} ev={} kind={} mem_id={} addr={:#x} value={:#x} lane={:?}", + eidx, kind, ev.twist_id.0, ev.addr, ev.value, ev.lane + ); + } + } + if row.shout_events.is_empty() { + println!("debug_row_shout idx={i} shout_events=[]"); + } else { + for (eidx, ev) in row.shout_events.iter().enumerate() { + println!( + "debug_row_shout idx={i} ev={} shout_id={} key={:#x} value={:#x}", + eidx, ev.shout_id.0, ev.key, ev.value + ); + } + } + } +} + +#[test] +#[ignore = "slow full-trace prove/verify benchmark"] +fn test_riscv_circuit_l2_transfer_compiled_trace_prove_verify_with_metrics() { + let program_base = circuit_l2_transfer_rom::CIRCUIT_L2_TRANSFER_ROM_BASE; + let program_bytes: &[u8] = &circuit_l2_transfer_rom::CIRCUIT_L2_TRANSFER_ROM; + let static_instruction_words = program_bytes.len() / 4; + let min_trace_len = static_instruction_words; + let max_steps = static_instruction_words; + let chunk_rows = 1500usize; + + let setup_wall_start = Instant::now(); + let decoded_program = decode_program(program_bytes).expect("decode circuit_l2_transfer ROM"); + let mut sim_cpu = RiscvCpu::new(32); + sim_cpu.load_program(program_base, decoded_program); + let sim_twist = RiscvMemory::with_program_in_twist(32, PROG_ID, program_base, program_bytes); + let sim_shout = RiscvShoutTables::new(32); + let sim_trace = trace_program(sim_cpu, sim_twist, sim_shout, max_steps) + .expect("trace circuit_l2_transfer ROM for pre-prove metrics"); + println!( + "trace_sim_steps={} trace_sim_did_halt={} trace_sim_total_twist_events={} trace_sim_total_shout_events={}", + sim_trace.len(), + sim_trace.did_halt(), + sim_trace.total_twist_events(), + sim_trace.total_shout_events() + ); + + let wiring = Rv32TraceWiring::from_rom(program_base, program_bytes) + .xlen(32) + .min_trace_len(min_trace_len) + .chunk_rows(chunk_rows) + .max_steps(max_steps) + .shout_auto_minimal(); + let setup_wall = setup_wall_start.elapsed(); + + let prove_wall_start = Instant::now(); + let prove_result = wiring.prove(); + let prove_wall = prove_wall_start.elapsed(); + + println!("==== circuit_l2_transfer metrics ===="); + println!( + "program_base={} rom_bytes={} static_instruction_words={} min_trace_len={} chunk_rows={} max_steps={}", + program_base, + program_bytes.len(), + static_instruction_words, + min_trace_len, + chunk_rows, + max_steps + ); + println!("setup_time_wall={:?}", setup_wall); + println!("prove_wall_time={:?}", prove_wall); + + let mut run = match prove_result { + Ok(run) => run, + Err(err) => { + let err_s = err.to_string(); + println!("trace_instructions_active_rows=N/A (prove failed)"); + println!("fold_steps=N/A (prove failed)"); + println!("ccs_constraints=N/A (prove failed)"); + println!("ccs_variables=N/A (prove failed)"); + println!("layout_t=N/A (prove failed)"); + println!("layout_m_in=N/A (prove failed)"); + println!("layout_m=N/A (prove failed)"); + println!("used_memory_ids=N/A (prove failed)"); + println!("used_shout_table_ids=N/A (prove failed)"); + println!("setup_time=N/A (prove failed)"); + println!("chunk_build_commit_time=N/A (prove failed)"); + println!("fold_and_prove_time=N/A (prove failed)"); + println!("prove_time_total=N/A (prove failed)"); + println!("verify_time=N/A (prove failed)"); + println!("verify_wall_time=N/A (prove failed)"); + println!("prove_error={err_s}"); + if let Some(row_idx) = parse_row_idx(&err_s) { + println!("debug_failure_row_idx={row_idx}"); + dump_exec_row_context(program_base, program_bytes, 1 << 20, row_idx); + } else { + println!("debug_failure_row_idx=unavailable"); + } + println!("====================================="); + panic!("prove circuit_l2_transfer failed: {err}"); + } + }; + + let phase = run.prove_phase_durations(); + let layout = run.layout(); + println!("trace_instructions_active_rows={}", run.trace_len()); + println!("fold_steps={}", run.fold_count()); + println!("trace_hit_max_steps_cap={}", run.trace_len() == max_steps); + println!( + "ccs_constraints={} ccs_variables={}", + run.ccs_num_constraints(), + run.ccs_num_variables() + ); + println!( + "layout_t={} layout_m_in={} layout_m={}", + layout.t, layout.m_in, layout.m + ); + println!("used_memory_ids={:?}", run.used_memory_ids()); + println!("used_shout_table_ids={:?}", run.used_shout_table_ids()); + println!("setup_time={:?}", phase.setup); + println!("chunk_build_commit_time={:?}", phase.chunk_build_commit); + println!("fold_and_prove_time={:?}", phase.fold_and_prove); + println!("prove_time_total={:?}", run.prove_duration()); + + let verify_wall_start = Instant::now(); + let verify_result = run.verify(); + let verify_wall = verify_wall_start.elapsed(); + println!("verify_time={:?}", run.verify_duration().unwrap_or(verify_wall)); + println!("verify_wall_time={:?}", verify_wall); + if let Err(err) = verify_result { + println!("verify_error={err}"); + println!("====================================="); + panic!("verify circuit_l2_transfer failed: {err}"); + } + println!("====================================="); +} diff --git a/crates/neo-fold/src/memory_sidecar/claim_plan.rs b/crates/neo-fold/src/memory_sidecar/claim_plan.rs index b2a9abb0..608d0350 100644 --- a/crates/neo-fold/src/memory_sidecar/claim_plan.rs +++ b/crates/neo-fold/src/memory_sidecar/claim_plan.rs @@ -302,7 +302,7 @@ impl RouteATimeClaimPlan { if decode_stage_enabled { out.push(TimeClaimMeta { label: b"decode/fields", - degree_bound: 4, + degree_bound: 5, is_dynamic: false, }); out.push(TimeClaimMeta { @@ -325,7 +325,7 @@ impl RouteATimeClaimPlan { }); out.push(TimeClaimMeta { label: b"width/load_semantics", - degree_bound: 4, + degree_bound: 5, is_dynamic: false, }); out.push(TimeClaimMeta { @@ -353,7 +353,7 @@ impl RouteATimeClaimPlan { }); out.push(TimeClaimMeta { label: b"control/writeback", - degree_bound: 4, + degree_bound: 5, is_dynamic: false, }); } diff --git a/crates/neo-fold/src/memory_sidecar/memory/addr_pre_proofs.rs b/crates/neo-fold/src/memory_sidecar/memory/addr_pre_proofs.rs index 01021860..92541750 100644 --- a/crates/neo-fold/src/memory_sidecar/memory/addr_pre_proofs.rs +++ b/crates/neo-fold/src/memory_sidecar/memory/addr_pre_proofs.rs @@ -80,14 +80,10 @@ pub(crate) fn prove_shout_addr_pre_time( let z = &cpu_z_k; let inst_ell_addr = lut_inst.d * lut_inst.ell; - if matches!( + let is_packed_spec = matches!( lut_inst.table_spec, Some(LutTableSpec::RiscvOpcodePacked { .. } | LutTableSpec::RiscvOpcodeEventTablePacked { .. }) - ) { - return Err(PiCcsError::InvalidInput( - "packed RISC-V Shout table specs are not supported on the shared CPU bus".into(), - )); - } + ); let inst_ell_addr_u32 = u32::try_from(inst_ell_addr) .map_err(|_| PiCcsError::InvalidInput("Shout(Route A): ell_addr overflows u32".into()))?; groups @@ -193,7 +189,7 @@ pub(crate) fn prove_shout_addr_pre_time( SparseIdxVec::new(pow2_cycle) }; - if has_any_lookup { + if has_any_lookup && !is_packed_spec { let (addr_oracle, lane_sum): (Box, K) = match &lut_inst.table_spec { None => { let table_k: Vec = lut_inst.table.iter().map(|&v| v.into()).collect(); @@ -211,14 +207,10 @@ pub(crate) fn prove_shout_addr_pre_time( )?; (Box::new(o), sum) } - Some(LutTableSpec::RiscvOpcodePacked { .. }) => { - return Err(PiCcsError::InvalidInput( - "packed RISC-V Shout table specs are not supported on the shared CPU bus".into(), - )); - } - Some(LutTableSpec::RiscvOpcodeEventTablePacked { .. }) => { - return Err(PiCcsError::InvalidInput( - "packed RISC-V Shout table specs are not supported on the shared CPU bus".into(), + Some(LutTableSpec::RiscvOpcodePacked { .. }) + | Some(LutTableSpec::RiscvOpcodeEventTablePacked { .. }) => { + return Err(PiCcsError::ProtocolError( + "packed shout lane unexpectedly reached implicit addr-pre oracle path".into(), )); } Some(LutTableSpec::IdentityU32) => { diff --git a/crates/neo-fold/src/memory_sidecar/memory/route_a_claim_builders.rs b/crates/neo-fold/src/memory_sidecar/memory/route_a_claim_builders.rs index e57012f1..8b82c3a1 100644 --- a/crates/neo-fold/src/memory_sidecar/memory/route_a_claim_builders.rs +++ b/crates/neo-fold/src/memory_sidecar/memory/route_a_claim_builders.rs @@ -177,6 +177,185 @@ pub(crate) fn build_route_a_width_time_claims( decoded }; + // Defensive row-wise validation: width-sidecar residual families must vanish before + // entering batched sumcheck. This turns opaque aggregate failures into actionable row/idx errors. + for j in 0..t_len { + let rd_val = *main_decoded + .get(&trace.rd_val) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing rd_val row while validating".into()))?; + let ram_rv = *main_decoded + .get(&trace.ram_rv) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing ram_rv row while validating".into()))?; + let ram_wv = *main_decoded + .get(&trace.ram_wv) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing ram_wv row while validating".into()))?; + let rs2_val = *main_decoded + .get(&trace.rs2_val) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing rs2_val row while validating".into()))?; + let active = *main_decoded + .get(&trace.active) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing active row while validating".into()))?; + + let rd_has_write = *decode_decoded + .get(&decode.rd_has_write) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing rd_has_write row while validating".into()))?; + let ram_has_read = *decode_decoded + .get(&decode.ram_has_read) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing ram_has_read row while validating".into()))?; + let ram_has_write = *decode_decoded + .get(&decode.ram_has_write) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing ram_has_write row while validating".into()) + })?; + let op_load = *decode_decoded + .get(&decode.op_load) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing op_load row while validating".into()))?; + let op_store = *decode_decoded + .get(&decode.op_store) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing op_store row while validating".into()))?; + let funct3_is = [ + *decode_decoded + .get(&decode.funct3_is[0]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[0] row while validating".into()) + })?, + *decode_decoded + .get(&decode.funct3_is[1]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[1] row while validating".into()) + })?, + *decode_decoded + .get(&decode.funct3_is[2]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[2] row while validating".into()) + })?, + *decode_decoded + .get(&decode.funct3_is[3]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[3] row while validating".into()) + })?, + *decode_decoded + .get(&decode.funct3_is[4]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[4] row while validating".into()) + })?, + *decode_decoded + .get(&decode.funct3_is[5]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[5] row while validating".into()) + })?, + *decode_decoded + .get(&decode.funct3_is[6]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[6] row while validating".into()) + })?, + *decode_decoded + .get(&decode.funct3_is[7]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("W3(shared): missing funct3_is[7] row while validating".into()) + })?, + ]; + let ram_rv_q16 = *width_decoded + .get(&width.ram_rv_q16) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing ram_rv_q16 row while validating".into()))?; + let rs2_q16 = *width_decoded + .get(&width.rs2_q16) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("W3(shared): missing rs2_q16 row while validating".into()))?; + let mut ram_rv_low_bits = [K::ZERO; 16]; + let mut rs2_low_bits = [K::ZERO; 16]; + for bit in 0..16usize { + ram_rv_low_bits[bit] = *width_decoded + .get(&width.ram_rv_low_bit[bit]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError(format!( + "W3(shared): missing ram_rv_low_bit[{bit}] row while validating" + )) + })?; + rs2_low_bits[bit] = *width_decoded + .get(&width.rs2_low_bit[bit]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError(format!("W3(shared): missing rs2_low_bit[{bit}] row while validating")) + })?; + } + + let load_flags = [ + op_load * funct3_is[0], + op_load * funct3_is[4], + op_load * funct3_is[1], + op_load * funct3_is[5], + op_load * funct3_is[2], + ]; + let load_residuals = w3_load_semantics_residuals( + rd_val, + ram_rv, + rd_has_write, + ram_has_read, + load_flags, + ram_rv_q16, + ram_rv_low_bits, + ); + if let Some((idx, _)) = load_residuals + .iter() + .enumerate() + .find(|(_, r)| **r != K::ZERO) + { + return Err(PiCcsError::ProtocolError(format!( + "w3/load_semantics residual non-zero at row={j}, idx={idx}, active={active}, op_load={op_load}, funct3_is={:?}, rd_has_write={rd_has_write}, ram_has_read={ram_has_read}", + funct3_is + ))); + } + + let store_flags = [ + op_store * funct3_is[0], + op_store * funct3_is[1], + op_store * funct3_is[2], + ]; + let store_residuals = w3_store_semantics_residuals( + ram_wv, + ram_rv, + rs2_val, + rd_has_write, + ram_has_read, + ram_has_write, + store_flags, + rs2_q16, + ram_rv_low_bits, + rs2_low_bits, + ); + if let Some((idx, _)) = store_residuals + .iter() + .enumerate() + .find(|(_, r)| **r != K::ZERO) + { + return Err(PiCcsError::ProtocolError(format!( + "w3/store_semantics residual non-zero at row={j}, idx={idx}, active={active}, op_store={op_store}, funct3_is={:?}, ram_has_read={ram_has_read}, ram_has_write={ram_has_write}", + funct3_is + ))); + } + } + let mut main_sparse = BTreeMap::>::new(); for &col_id in main_col_ids.iter() { let vals = main_decoded @@ -280,7 +459,7 @@ pub(crate) fn build_route_a_width_time_claims( let load_weights = w3_load_weight_vector(r_cycle, 16); let load_oracle = FormulaOracleSparseTime::new( load_sparse, - 4, + 5, r_cycle, Box::new(move |vals: &[K]| { let rd_val = vals[0]; @@ -520,6 +699,244 @@ pub(crate) fn build_route_a_control_time_claims( decoded }; + for j in 0..t_len { + let active = *main_decoded + .get(&trace.active) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing active row while validating".into()))?; + let pc_before = *main_decoded + .get(&trace.pc_before) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing pc_before row while validating".into()) + })?; + let pc_after = *main_decoded + .get(&trace.pc_after) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing pc_after row while validating".into()) + })?; + let rs1_val = *main_decoded + .get(&trace.rs1_val) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs1_val row while validating".into()))?; + let jalr_drop_bit = *main_decoded + .get(&trace.jalr_drop_bit) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing jalr_drop_bit row while validating".into()) + })?; + let shout_val = *main_decoded + .get(&trace.shout_val) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing shout_val row while validating".into()) + })?; + let imm_i = *decode_decoded + .get(&decode.imm_i) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing imm_i row while validating".into()))?; + let imm_b = *decode_decoded + .get(&decode.imm_b) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing imm_b row while validating".into()))?; + let imm_j = *decode_decoded + .get(&decode.imm_j) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing imm_j row while validating".into()))?; + let imm_sign_bit = *decode_decoded + .get(&decode.funct7_bit[6]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing funct7_bit[6] row while validating".into()) + })?; + let op_jal = *decode_decoded + .get(&decode.op_jal) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing op_jal row while validating".into()))?; + let op_jalr = *decode_decoded + .get(&decode.op_jalr) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing op_jalr row while validating".into()))?; + let op_branch = *decode_decoded + .get(&decode.op_branch) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing op_branch row while validating".into()) + })?; + let funct3_bit0 = *decode_decoded + .get(&decode.funct3_bit[0]) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing funct3_bit[0] row while validating".into()) + })?; + let residuals = control_next_pc_control_residuals( + active, + pc_before, + pc_after, + rs1_val, + jalr_drop_bit, + imm_i, + imm_b, + imm_j, + imm_sign_bit, + op_jal, + op_jalr, + op_branch, + shout_val, + funct3_bit0, + ); + if let Some((idx, _)) = residuals.iter().enumerate().find(|(_, r)| **r != K::ZERO) { + return Err(PiCcsError::ProtocolError(format!( + "control/next_pc_control residual non-zero at row={j}, idx={idx}, active={active}, op_jal={op_jal}, op_jalr={op_jalr}, op_branch={op_branch}, jalr_drop_bit={jalr_drop_bit}, pc_before={pc_before}, pc_after={pc_after}, rs1_val={rs1_val}, imm_i={imm_i}, imm_b={imm_b}, imm_j={imm_j}, imm_sign_bit={imm_sign_bit}, shout_val={shout_val}, funct3_bit0={funct3_bit0}" + ))); + } + } + + for j in 0..t_len { + let rd_val = *main_decoded + .get(&trace.rd_val) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rd_val row while validating".into()))?; + let pc_before = *main_decoded + .get(&trace.pc_before) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing pc_before row while validating".into()) + })?; + let op_lui = *decode_decoded + .get(&decode.op_lui) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing op_lui row while validating".into()))?; + let op_auipc = *decode_decoded + .get(&decode.op_auipc) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing op_auipc row while validating".into()) + })?; + let op_jal = *decode_decoded + .get(&decode.op_jal) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing op_jal row while validating".into()))?; + let op_jalr = *decode_decoded + .get(&decode.op_jalr) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing op_jalr row while validating".into()))?; + let rd_is_zero = *decode_decoded + .get(&decode.rd_is_zero) + .and_then(|v| v.get(j)) + .ok_or_else(|| { + PiCcsError::ProtocolError("control(shared): missing rd_is_zero row while validating".into()) + })?; + let funct3_bits = [ + *decode_decoded + .get(&decode.funct3_bit[0]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct3_bit[0] row".into()))?, + *decode_decoded + .get(&decode.funct3_bit[1]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct3_bit[1] row".into()))?, + *decode_decoded + .get(&decode.funct3_bit[2]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct3_bit[2] row".into()))?, + ]; + let rs1_bits = [ + *decode_decoded + .get(&decode.rs1_bit[0]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs1_bit[0] row".into()))?, + *decode_decoded + .get(&decode.rs1_bit[1]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs1_bit[1] row".into()))?, + *decode_decoded + .get(&decode.rs1_bit[2]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs1_bit[2] row".into()))?, + *decode_decoded + .get(&decode.rs1_bit[3]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs1_bit[3] row".into()))?, + *decode_decoded + .get(&decode.rs1_bit[4]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs1_bit[4] row".into()))?, + ]; + let rs2_bits = [ + *decode_decoded + .get(&decode.rs2_bit[0]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs2_bit[0] row".into()))?, + *decode_decoded + .get(&decode.rs2_bit[1]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs2_bit[1] row".into()))?, + *decode_decoded + .get(&decode.rs2_bit[2]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs2_bit[2] row".into()))?, + *decode_decoded + .get(&decode.rs2_bit[3]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs2_bit[3] row".into()))?, + *decode_decoded + .get(&decode.rs2_bit[4]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing rs2_bit[4] row".into()))?, + ]; + let funct7_bits = [ + *decode_decoded + .get(&decode.funct7_bit[0]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct7_bit[0] row".into()))?, + *decode_decoded + .get(&decode.funct7_bit[1]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct7_bit[1] row".into()))?, + *decode_decoded + .get(&decode.funct7_bit[2]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct7_bit[2] row".into()))?, + *decode_decoded + .get(&decode.funct7_bit[3]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct7_bit[3] row".into()))?, + *decode_decoded + .get(&decode.funct7_bit[4]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct7_bit[4] row".into()))?, + *decode_decoded + .get(&decode.funct7_bit[5]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct7_bit[5] row".into()))?, + *decode_decoded + .get(&decode.funct7_bit[6]) + .and_then(|v| v.get(j)) + .ok_or_else(|| PiCcsError::ProtocolError("control(shared): missing funct7_bit[6] row".into()))?, + ]; + let imm_u = control_imm_u_from_bits(funct3_bits, rs1_bits, rs2_bits, funct7_bits); + let op_lui_write = op_lui * (K::ONE - rd_is_zero); + let op_auipc_write = op_auipc * (K::ONE - rd_is_zero); + let op_jal_write = op_jal * (K::ONE - rd_is_zero); + let op_jalr_write = op_jalr * (K::ONE - rd_is_zero); + let residuals = control_writeback_residuals( + rd_val, + pc_before, + imm_u, + op_lui_write, + op_auipc_write, + op_jal_write, + op_jalr_write, + ); + if let Some((idx, _)) = residuals.iter().enumerate().find(|(_, r)| **r != K::ZERO) { + return Err(PiCcsError::ProtocolError(format!( + "control/writeback residual non-zero at row={j}, idx={idx}, rd_val={rd_val}, pc_before={pc_before}, imm_u={imm_u}, op_lui={op_lui}, op_auipc={op_auipc}, op_jal={op_jal}, op_jalr={op_jalr}, rd_is_zero={rd_is_zero}" + ))); + } + } + let mut main_sparse = BTreeMap::>::new(); for &col_id in main_col_ids.iter() { let vals = main_decoded @@ -588,6 +1005,7 @@ pub(crate) fn build_route_a_control_time_claims( decode_col(decode.imm_i)?, decode_col(decode.imm_b)?, decode_col(decode.imm_j)?, + decode_col(decode.funct7_bit[6])?, ]; let control_weights = control_next_pc_control_weight_vector(r_cycle, 5); let control_oracle = FormulaOracleSparseTime::new( @@ -596,8 +1014,8 @@ pub(crate) fn build_route_a_control_time_claims( r_cycle, Box::new(move |vals: &[K]| { let residuals = control_next_pc_control_residuals( - vals[0], vals[1], vals[2], vals[3], vals[4], vals[10], vals[11], vals[12], vals[7], vals[8], vals[9], - vals[5], vals[6], + vals[0], vals[1], vals[2], vals[3], vals[4], vals[10], vals[11], vals[12], vals[13], vals[7], vals[8], + vals[9], vals[5], vals[6], ); let mut weighted = K::ZERO; for (r, w) in residuals.iter().zip(control_weights.iter()) { @@ -656,7 +1074,7 @@ pub(crate) fn build_route_a_control_time_claims( let write_weights = control_writeback_weight_vector(r_cycle, 4); let write_oracle = FormulaOracleSparseTime::new( write_sparse, - 4, + 5, r_cycle, Box::new(move |vals: &[K]| { let rd_val = vals[0]; diff --git a/crates/neo-fold/src/memory_sidecar/memory/route_a_claims.rs b/crates/neo-fold/src/memory_sidecar/memory/route_a_claims.rs index 2d6a322d..aa09371b 100644 --- a/crates/neo-fold/src/memory_sidecar/memory/route_a_claims.rs +++ b/crates/neo-fold/src/memory_sidecar/memory/route_a_claims.rs @@ -615,9 +615,27 @@ pub(crate) fn build_route_a_decode_time_claims( let rd_is_zero_vals = decoded .get(&decode.rd_is_zero) .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing rd_is_zero decode column".into()))?; + let funct7_b0_vals = decoded + .get(&decode.funct7_bit[0]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct7_bit[0] decode column".into()))?; + let funct7_b1_vals = decoded + .get(&decode.funct7_bit[1]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct7_bit[1] decode column".into()))?; + let funct7_b2_vals = decoded + .get(&decode.funct7_bit[2]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct7_bit[2] decode column".into()))?; + let funct7_b3_vals = decoded + .get(&decode.funct7_bit[3]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct7_bit[3] decode column".into()))?; + let funct7_b4_vals = decoded + .get(&decode.funct7_bit[4]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct7_bit[4] decode column".into()))?; let funct7_b5_vals = decoded .get(&decode.funct7_bit[5]) .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct7_bit[5] decode column".into()))?; + let funct7_b6_vals = decoded + .get(&decode.funct7_bit[6]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct7_bit[6] decode column".into()))?; let op_lui_vals = decoded .get(&decode.op_lui) .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing op_lui decode column".into()))?; @@ -642,9 +660,24 @@ pub(crate) fn build_route_a_decode_time_claims( let funct3_is1_vals = decoded .get(&decode.funct3_is[1]) .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct3_is[1] decode column".into()))?; + let funct3_is2_vals = decoded + .get(&decode.funct3_is[2]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct3_is[2] decode column".into()))?; + let funct3_is3_vals = decoded + .get(&decode.funct3_is[3]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct3_is[3] decode column".into()))?; + let funct3_is4_vals = decoded + .get(&decode.funct3_is[4]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct3_is[4] decode column".into()))?; let funct3_is5_vals = decoded .get(&decode.funct3_is[5]) .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct3_is[5] decode column".into()))?; + let funct3_is6_vals = decoded + .get(&decode.funct3_is[6]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct3_is[6] decode column".into()))?; + let funct3_is7_vals = decoded + .get(&decode.funct3_is[7]) + .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing funct3_is[7] decode column".into()))?; let rs2_vals = decoded .get(&decode.rs2) .ok_or_else(|| PiCcsError::ProtocolError("W2(shared): missing rs2 decode column".into()))?; @@ -669,8 +702,27 @@ pub(crate) fn build_route_a_decode_time_claims( op_jalr_write.push(op_jalr_vals[j] * rd_keep); op_alu_imm_write.push(op_alu_imm_vals[j] * rd_keep); op_alu_reg_write.push(op_alu_reg_vals[j] * rd_keep); - alu_reg_delta.push(funct7_b5_vals[j] * (funct3_is0_vals[j] + funct3_is5_vals[j])); - alu_imm_delta.push(funct7_b5_vals[j] * funct3_is5_vals[j]); + let funct7_bits = [ + funct7_b0_vals[j], + funct7_b1_vals[j], + funct7_b2_vals[j], + funct7_b3_vals[j], + funct7_b4_vals[j], + funct7_b5_vals[j], + funct7_b6_vals[j], + ]; + let funct3_is = [ + funct3_is0_vals[j], + funct3_is1_vals[j], + funct3_is2_vals[j], + funct3_is3_vals[j], + funct3_is4_vals[j], + funct3_is5_vals[j], + funct3_is6_vals[j], + funct3_is7_vals[j], + ]; + alu_reg_delta.push(w2_alu_reg_table_delta_from_bits(funct7_bits, funct3_is)); + alu_imm_delta.push(funct7_bits[5] * funct3_is[5]); alu_imm_shift_rhs_delta.push((funct3_is1_vals[j] + funct3_is5_vals[j]) * (rs2_vals[j] - imm_i_vals[j])); } decoded.insert(decode.op_lui_write, op_lui_write); @@ -801,7 +853,7 @@ pub(crate) fn build_route_a_decode_time_claims( opcode_flags[8] * (K::ONE - rd_is_zero), ]; let shout_table_id = decode_value_at(decode.shout_table_id, j)?; - let alu_reg_table_delta = funct7_bits[5] * (funct3_is[0] + funct3_is[5]); + let alu_reg_table_delta = w2_alu_reg_table_delta_from_bits(funct7_bits, funct3_is); let alu_imm_table_delta = funct7_bits[5] * funct3_is[5]; let alu_imm_shift_rhs_delta = (funct3_is[1] + funct3_is[5]) * (rs2_decode - imm_i); let selector_residuals = w2_decode_selector_residuals( @@ -974,7 +1026,7 @@ pub(crate) fn build_route_a_decode_time_claims( let fields_weights = w2_decode_pack_weight_vector(r_cycle, W2_FIELDS_RESIDUAL_COUNT); let fields_oracle = FormulaOracleSparseTime::new( fields_sparse_cols, - 4, + 5, r_cycle, Box::new(move |vals: &[K]| { let mut idx = 0usize; @@ -1062,7 +1114,7 @@ pub(crate) fn build_route_a_decode_time_claims( opcode_flags[7] * rd_keep, opcode_flags[8] * rd_keep, ]; - let alu_reg_table_delta = funct7_bits[5] * (funct3_is[0] + funct3_is[5]); + let alu_reg_table_delta = w2_alu_reg_table_delta_from_bits(funct7_bits, funct3_is); let alu_imm_table_delta = funct7_bits[5] * funct3_is[5]; let alu_imm_shift_rhs_delta = (funct3_is[1] + funct3_is[5]) * (rs2_decode - imm_i); let selector_residuals = w2_decode_selector_residuals( diff --git a/crates/neo-fold/src/memory_sidecar/memory/route_a_terminal_checks.rs b/crates/neo-fold/src/memory_sidecar/memory/route_a_terminal_checks.rs index b7051636..ccdd3e15 100644 --- a/crates/neo-fold/src/memory_sidecar/memory/route_a_terminal_checks.rs +++ b/crates/neo-fold/src/memory_sidecar/memory/route_a_terminal_checks.rs @@ -162,7 +162,7 @@ pub(crate) fn verify_route_a_decode_terminals( opcode_flags[7] * (K::ONE - rd_is_zero), opcode_flags[8] * (K::ONE - rd_is_zero), ]; - let alu_reg_table_delta = funct7_bits[5] * (funct3_is[0] + funct3_is[5]); + let alu_reg_table_delta = w2_alu_reg_table_delta_from_bits(funct7_bits, funct3_is); let alu_imm_table_delta = funct7_bits[5] * funct3_is[5]; let rs2_decode = decode_open_col(decode_layout.rs2)?; let imm_i = decode_open_col(decode_layout.imm_i)?; @@ -759,6 +759,7 @@ pub(crate) fn verify_route_a_control_terminals( imm_i, imm_b, imm_j, + funct7_bits[6], op_jal, op_jalr, op_branch, diff --git a/crates/neo-fold/src/memory_sidecar/memory/route_a_verify.rs b/crates/neo-fold/src/memory_sidecar/memory/route_a_verify.rs index 4054c2b3..582e8e05 100644 --- a/crates/neo-fold/src/memory_sidecar/memory/route_a_verify.rs +++ b/crates/neo-fold/src/memory_sidecar/memory/route_a_verify.rs @@ -197,14 +197,23 @@ pub fn verify_route_a_memory_step( MemOrLutProof::Shout(_proof) => {} _ => return Err(PiCcsError::InvalidInput("expected Shout proof".into())), } - if matches!( - inst.table_spec, - Some(LutTableSpec::RiscvOpcodePacked { .. } | LutTableSpec::RiscvOpcodeEventTablePacked { .. }) - ) { + let packed_layout = rv32_packed_shout_layout(&inst.table_spec)?; + if matches!(packed_layout, Some((_op, time_bits)) if time_bits != 0) { return Err(PiCcsError::InvalidInput( - "packed RISC-V Shout table specs are not supported on the shared CPU bus".into(), + "RiscvOpcodeEventTablePacked is not supported in shared-bus Route-A verification".into(), )); } + let packed_opcode = match &inst.table_spec { + Some(LutTableSpec::RiscvOpcodePacked { opcode, xlen }) => { + if *xlen != 32 { + return Err(PiCcsError::InvalidInput(format!( + "RiscvOpcodePacked requires xlen=32 in Route-A verification (got xlen={xlen})" + ))); + } + Some(*opcode) + } + _ => None, + }; let ell_addr = inst.d * inst.ell; let expected_lanes = inst.lanes.max(1); @@ -298,10 +307,23 @@ pub fn verify_route_a_memory_step( // - adapter (time rounds only) per lane // - aggregated bitness for (addr_bits, has_lookup) { - let mut opens: Vec = Vec::with_capacity(expected_lanes * (ell_addr + 1)); - for lane in lane_opens.iter() { - opens.extend_from_slice(&lane.addr_bits); - opens.push(lane.has_lookup); + let mut opens: Vec = Vec::new(); + if let Some(op) = packed_opcode { + for lane in lane_opens.iter() { + let mut lane_terms = neo_memory::riscv::packed::rv32_collect_packed_bitness_terms( + op, + lane.addr_bits.as_slice(), + lane.has_lookup, + lane.val, + )?; + opens.append(&mut lane_terms); + } + } else { + opens.reserve(expected_lanes * (ell_addr + 1)); + for lane in lane_opens.iter() { + opens.extend_from_slice(&lane.addr_bits); + opens.push(lane.has_lookup); + } } let weights = bitness_weights(r_cycle, opens.len(), 0x5348_4F55_54u64 + proof_idx as u64); let mut acc = K::ZERO; @@ -321,7 +343,17 @@ pub fn verify_route_a_memory_step( shout_trace_sums.has_lookup += lane.has_lookup; shout_trace_sums.val += lane.val; shout_trace_sums.table_id += lane.has_lookup * lane_table_id; - let (lhs, rhs) = unpack_interleaved_halves_lsb(&lane.addr_bits)?; + let (lhs, rhs) = if packed_opcode.is_some() { + let lhs = *lane.addr_bits.first().ok_or_else(|| { + PiCcsError::InvalidInput("packed Shout trace linkage requires lhs in addr_bits[0]".into()) + })?; + let rhs = *lane.addr_bits.get(1).ok_or_else(|| { + PiCcsError::InvalidInput("packed Shout trace linkage requires rhs in addr_bits[1]".into()) + })?; + (lhs, rhs) + } else { + unpack_interleaved_halves_lsb(&lane.addr_bits)? + }; if lane.shared_addr_group { let inv_count = K::from_u64(lane.shared_addr_group_size as u64).inverse(); shout_trace_sums.lhs += lhs * inv_count; @@ -344,6 +376,11 @@ pub fn verify_route_a_memory_step( .ok_or_else(|| PiCcsError::ProtocolError("shout claim schedule lane idx drift".into()))?; if lane_claims.gamma_group.is_some() { + if packed_opcode.is_some() { + return Err(PiCcsError::ProtocolError( + "packed shout lane unexpectedly assigned to gamma group".into(), + )); + } if !pre.is_active { if pre.addr_claim_sum != K::ZERO || pre.addr_final != K::ZERO || lane.has_lookup != K::ZERO { return Err(PiCcsError::ProtocolError( @@ -369,6 +406,27 @@ pub fn verify_route_a_memory_step( let adapter_claim = batched_claimed_sums[adapter_idx]; let adapter_final = batched_final_values[adapter_idx]; + if packed_opcode.is_some() { + // Packed Route-A lanes are verified as zero-sum constraints. The claimed sums + // must be zero, but terminal evaluations at the sampled random point are not + // required to be zero in general. + if value_claim != K::ZERO || adapter_claim != K::ZERO { + return Err(PiCcsError::ProtocolError(format!( + "packed shout lane zero-claim invariant mismatch at lut_idx={proof_idx}, lane_idx={lane_idx}: value_claim={value_claim:?}, adapter_claim={adapter_claim:?}" + ))); + } + if pre.is_active + || pre.addr_claim_sum != K::ZERO + || pre.addr_final != K::ZERO + || pre.table_eval_at_r_addr != K::ZERO + { + return Err(PiCcsError::ProtocolError( + "packed shout lane addr-pre invariants mismatch".into(), + )); + } + continue; + } + let expected_value_final = chi_cycle_at_r_time * lane.has_lookup * lane.val; if expected_value_final != value_final { return Err(PiCcsError::ProtocolError("shout value terminal value mismatch".into())); diff --git a/crates/neo-fold/src/memory_sidecar/memory/transcript_and_common.rs b/crates/neo-fold/src/memory_sidecar/memory/transcript_and_common.rs index 1c1cef55..0c5ef1ef 100644 --- a/crates/neo-fold/src/memory_sidecar/memory/transcript_and_common.rs +++ b/crates/neo-fold/src/memory_sidecar/memory/transcript_and_common.rs @@ -901,6 +901,23 @@ pub(crate) fn w2_decode_bitness_residuals(opcode_flags: [K; 12], funct3_is: [K; ] } +#[inline] +pub(crate) fn w2_alu_reg_table_delta_from_bits(funct7_bits: [K; 7], funct3_is: [K; 8]) -> K { + let is_rv32m = funct7_bits[0]; + + let base_delta = funct7_bits[5] * (funct3_is[0] + funct3_is[5]); + let rv32m_delta = K::from(F::from_u64(9)) * funct3_is[0] + + K::from(F::from_u64(6)) * funct3_is[1] + + K::from(F::from_u64(10)) * funct3_is[2] + + K::from(F::from_u64(8)) * funct3_is[3] + + K::from(F::from_u64(15)) * funct3_is[4] + + K::from(F::from_u64(9)) * funct3_is[5] + + K::from(F::from_u64(16)) * funct3_is[6] + + K::from(F::from_u64(19)) * funct3_is[7]; + + (K::ONE - is_rv32m) * base_delta + is_rv32m * rv32m_delta +} + #[inline] pub(crate) fn w2_alu_branch_lookup_residuals( active: K, @@ -935,6 +952,8 @@ pub(crate) fn w2_alu_branch_lookup_residuals( let op_jal = opcode_flags[2]; let op_jalr = opcode_flags[3]; let op_branch = opcode_flags[4]; + let op_load = opcode_flags[5]; + let op_store = opcode_flags[6]; let op_alu_imm = opcode_flags[7]; let op_alu_reg = opcode_flags[8]; let op_misc_mem = opcode_flags[9]; @@ -949,6 +968,9 @@ pub(crate) fn w2_alu_branch_lookup_residuals( let non_mem_ops = op_lui + op_auipc + op_jal + op_jalr + op_branch + op_alu_imm + op_alu_reg + op_misc_mem + op_system; + let mem_lookup_ops = op_load + op_store; + let add_lookup_ops = op_load + op_store + op_jalr; + let add_table_id = K::from(F::from_u64(3)); let alu_table_base = K::from(F::from_u64(3)) * funct3_is[0] + K::from(F::from_u64(7)) * funct3_is[1] @@ -960,24 +982,29 @@ pub(crate) fn w2_alu_branch_lookup_residuals( let branch_table_expected = K::from(F::from_u64(10)) - K::from(F::from_u64(5)) * funct3_bits[2] + (funct3_bits[1] * funct3_bits[2]); let shift_selector = funct3_is[1] + funct3_is[5]; + let funct7_m_tail = + funct7_bits[1] + funct7_bits[2] + funct7_bits[3] + funct7_bits[4] + funct7_bits[5] + funct7_bits[6]; + let alu_reg_table_delta_expected = w2_alu_reg_table_delta_from_bits(funct7_bits, funct3_is); [ - op_alu_imm * (shout_has_lookup - K::ONE), - op_alu_reg * (shout_has_lookup - K::ONE), + (op_alu_imm + op_load + op_jalr) * (shout_has_lookup - K::ONE), + (op_alu_reg + op_store) * (shout_has_lookup - K::ONE), op_branch * (shout_has_lookup - K::ONE), (K::ONE - shout_has_lookup) * shout_table_id, - (op_alu_imm + op_alu_reg + op_branch) * (shout_lhs - rs1_val), + (op_alu_imm + op_alu_reg + op_branch + mem_lookup_ops + op_jalr) * (shout_lhs - rs1_val), alu_imm_shift_rhs_delta - shift_selector * (rs2_decode - imm_i), - op_alu_imm * (shout_rhs - imm_i - alu_imm_shift_rhs_delta), - op_alu_reg * (shout_rhs - rs2_val), + op_alu_imm * (shout_rhs - imm_i - alu_imm_shift_rhs_delta) + (op_load + op_jalr) * (shout_rhs - imm_i), + op_alu_reg * (shout_rhs - rs2_val) + op_store * (shout_rhs - imm_s), op_branch * (shout_rhs - rs2_val), op_alu_imm_write * (rd_val - shout_val), op_alu_reg_write * (rd_val - shout_val), - op_alu_reg * (shout_table_id - alu_table_base - alu_reg_table_delta), - op_alu_imm * (shout_table_id - alu_table_base - alu_imm_table_delta), + op_alu_reg * (shout_table_id - alu_table_base - alu_reg_table_delta) + + op_store * (shout_table_id - add_table_id), + op_alu_imm * (shout_table_id - alu_table_base - alu_imm_table_delta) + + add_lookup_ops * (shout_table_id - add_table_id), op_branch * (shout_table_id - branch_table_expected), - op_alu_reg * funct7_bits[0], - alu_reg_table_delta - funct7_bits[5] * (funct3_is[0] + funct3_is[5]), + op_alu_reg * funct7_bits[0] * funct7_m_tail, + alu_reg_table_delta - alu_reg_table_delta_expected, alu_imm_table_delta - funct7_bits[5] * funct3_is[5], op_lui * rd_has_write - op_lui_write, op_auipc * rd_has_write - op_auipc_write, @@ -1002,8 +1029,10 @@ pub(crate) fn w2_alu_branch_lookup_residuals( non_mem_ops * ram_has_read, non_mem_ops * ram_has_write, non_mem_ops * ram_addr, - opcode_flags[5] * (ram_addr - rs1_val - imm_i), - opcode_flags[6] * (ram_addr - rs1_val - imm_s), + // RV32 effective addresses are modular (u32 wraparound). We therefore bind RAM addr to + // ADD lookup output (`shout_val`) instead of raw field addition `rs1 + imm`. + op_load * (ram_addr - shout_val), + op_store * (ram_addr - shout_val), ] } @@ -1131,18 +1160,19 @@ pub(crate) fn w3_load_semantics_residuals( } acc }; + let rd_write_gate = rd_has_write; [ - load_flags[4] * (rd_val - ram_rv), - load_flags[0] * (rd_val - lb_val), - load_flags[1] * (rd_val - ram_rv_low8), - load_flags[2] * (rd_val - lh_val), - load_flags[3] * (rd_val - ram_rv_low16), - load_flags[0] * (rd_has_write - K::ONE), - load_flags[1] * (rd_has_write - K::ONE), - load_flags[2] * (rd_has_write - K::ONE), - load_flags[3] * (rd_has_write - K::ONE), - load_flags[4] * (rd_has_write - K::ONE), + load_flags[4] * rd_write_gate * (rd_val - ram_rv), + load_flags[0] * rd_write_gate * (rd_val - lb_val), + load_flags[1] * rd_write_gate * (rd_val - ram_rv_low8), + load_flags[2] * rd_write_gate * (rd_val - lh_val), + load_flags[3] * rd_write_gate * (rd_val - ram_rv_low16), + load_flags[0] * rd_has_write * (rd_has_write - K::ONE), + load_flags[1] * rd_has_write * (rd_has_write - K::ONE), + load_flags[2] * rd_has_write * (rd_has_write - K::ONE), + load_flags[3] * rd_has_write * (rd_has_write - K::ONE), + load_flags[4] * rd_has_write * (rd_has_write - K::ONE), load_flags[0] * (ram_has_read - K::ONE), load_flags[1] * (ram_has_read - K::ONE), load_flags[2] * (ram_has_read - K::ONE), @@ -1254,11 +1284,12 @@ pub(crate) fn control_next_pc_control_residuals( active: K, pc_before: K, pc_after: K, - rs1_val: K, + _rs1_val: K, jalr_drop_bit: K, - imm_i: K, + _imm_i: K, imm_b: K, imm_j: K, + imm_sign_bit: K, op_jal: K, op_jalr: K, op_branch: K, @@ -1266,11 +1297,15 @@ pub(crate) fn control_next_pc_control_residuals( funct3_bit0: K, ) -> [K; 5] { let four = K::from(F::from_u64(4)); + let two32 = K::from(F::from_u64(1u64 << 32)); + let imm_b_signed = imm_b - two32 * imm_sign_bit; + let imm_j_signed = imm_j - two32 * imm_sign_bit; let taken = control_branch_taken_from_bits(shout_val, funct3_bit0); [ - op_jal * (pc_after - pc_before - imm_j), - op_jalr * (pc_after - rs1_val - imm_i + jalr_drop_bit), - op_branch * (pc_after - pc_before - four - taken * (imm_b - four)), + op_jal * (pc_after - pc_before - imm_j_signed), + // JALR target uses modular ADD lookup output (`shout_val`) plus alignment drop. + op_jalr * (pc_after - shout_val + jalr_drop_bit), + op_branch * (pc_after - pc_before - four - taken * (imm_b_signed - four)), op_jalr * jalr_drop_bit * (jalr_drop_bit - K::ONE), (active - op_jalr) * jalr_drop_bit, ] @@ -1303,11 +1338,16 @@ pub(crate) fn control_writeback_residuals( op_jalr_write: K, ) -> [K; 4] { let four = K::from(F::from_u64(4)); + let two32 = K::from(F::from_u64(1u64 << 32)); + let auipc_delta = rd_val - pc_before - imm_u; + let jal_delta = rd_val - pc_before - four; [ op_lui_write * (rd_val - imm_u), - op_auipc_write * (rd_val - pc_before - imm_u), - op_jal_write * (rd_val - pc_before - four), - op_jalr_write * (rd_val - pc_before - four), + // RV32 writeback values are modular u32; allow either exact sum (delta=0) + // or wrapped sum (delta=-2^32). + op_auipc_write * auipc_delta * (auipc_delta + two32), + op_jal_write * jal_delta * (jal_delta + two32), + op_jalr_write * jal_delta * (jal_delta + two32), ] } diff --git a/crates/neo-fold/src/riscv_trace_shard.rs b/crates/neo-fold/src/riscv_trace_shard.rs index 75ec868f..0bda613c 100644 --- a/crates/neo-fold/src/riscv_trace_shard.rs +++ b/crates/neo-fold/src/riscv_trace_shard.rs @@ -36,6 +36,7 @@ use neo_memory::riscv::exec_table::{Rv32ExecRow, Rv32ExecTable}; use neo_memory::riscv::lookups::{ decode_program, RiscvCpu, RiscvInstruction, RiscvMemory, RiscvOpcode, RiscvShoutTables, PROG_ID, RAM_ID, REG_ID, }; +use neo_memory::riscv::packed::{rv32_packed_d, rv32_packed_rollout_opcode}; use neo_memory::riscv::rom_init::prog_rom_layout_and_init_words; use neo_memory::riscv::trace::{ extract_twist_lanes_over_time, rv32_decode_lookup_backed_cols, rv32_decode_lookup_backed_row_from_instr_word, @@ -460,7 +461,10 @@ fn infer_required_trace_shout_opcodes(program: &[RiscvInstruction]) -> HashSet { + RiscvInstruction::RAlu { op, .. } => { + ops.insert(*op); + } + RiscvInstruction::IAlu { op, .. } => { ops.insert(*op); } RiscvInstruction::Branch { cond, .. } => { @@ -498,14 +502,18 @@ fn program_requires_width_lookup(program: &[RiscvInstruction]) -> bool { .any(|instr| matches!(instr, RiscvInstruction::Load { .. } | RiscvInstruction::Store { .. })) } -fn rv32_trace_table_specs(shout_ops: &HashSet) -> HashMap { +fn rv32_trace_table_specs(shout_ops: &HashSet) -> Result, PiCcsError> { let shout = RiscvShoutTables::new(32); let mut table_specs = HashMap::new(); for &op in shout_ops { let table_id = shout.opcode_to_id(op).0; - table_specs.insert(table_id, LutTableSpec::RiscvOpcode { opcode: op, xlen: 32 }); + if rv32_packed_rollout_opcode(op) { + table_specs.insert(table_id, LutTableSpec::RiscvOpcodePacked { opcode: op, xlen: 32 }); + } else { + table_specs.insert(table_id, LutTableSpec::RiscvOpcode { opcode: op, xlen: 32 }); + } } - table_specs + Ok(table_specs) } fn build_rv32_decode_lookup_tables( @@ -1076,9 +1084,7 @@ impl Rv32TraceWiring { } else { Vec::new() }; - let mut table_specs = rv32_trace_table_specs(&shout_ops); - let mut base_shout_table_ids: Vec = table_specs.keys().copied().collect(); - base_shout_table_ids.sort_unstable(); + let mut table_specs = rv32_trace_table_specs(&shout_ops)?; for (&table_id, spec) in &self.extra_lut_table_specs { if table_specs.contains_key(&table_id) { return Err(PiCcsError::InvalidInput(format!( @@ -1087,7 +1093,37 @@ impl Rv32TraceWiring { } table_specs.insert(table_id, spec.clone()); } + let mut base_shout_table_ids: Vec = table_specs + .iter() + .filter_map(|(&table_id, spec)| match spec { + LutTableSpec::RiscvOpcodePacked { .. } | LutTableSpec::RiscvOpcodeEventTablePacked { .. } => None, + _ => Some(table_id), + }) + .collect(); + base_shout_table_ids.sort_unstable(); let mut all_extra_shout_specs = self.extra_shout_bus_specs.clone(); + for (&table_id, spec) in &table_specs { + match spec { + LutTableSpec::RiscvOpcodePacked { opcode, xlen } => { + if *xlen != 32 { + return Err(PiCcsError::InvalidInput(format!( + "RiscvOpcodePacked requires xlen=32 in RV32 trace mode (table_id={table_id}, xlen={xlen})" + ))); + } + all_extra_shout_specs.push(TraceShoutBusSpec { + table_id, + ell_addr: rv32_packed_d(*opcode)?, + n_vals: 1usize, + }); + } + LutTableSpec::RiscvOpcodeEventTablePacked { .. } => { + return Err(PiCcsError::InvalidInput( + "RiscvOpcodeEventTablePacked is not supported in RV32 trace shared-bus mode".into(), + )); + } + _ => {} + } + } all_extra_shout_specs.extend(decode_lookup_bus_specs.clone()); all_extra_shout_specs.extend(width_lookup_bus_specs.clone()); for spec in &all_extra_shout_specs { diff --git a/crates/neo-fold/src/session/circuit.rs b/crates/neo-fold/src/session/circuit.rs index 6b8e8cca..6e81ebc2 100644 --- a/crates/neo-fold/src/session/circuit.rs +++ b/crates/neo-fold/src/session/circuit.rs @@ -289,8 +289,15 @@ fn shout_meta_for_bus( .ok_or_else(|| "2*xlen overflow for RISC-V shout table".to_string())?; Ok((d, 2usize)) } - LutTableSpec::RiscvOpcodePacked { .. } => { - Err("RiscvOpcodePacked is not supported in shared-bus circuits".into()) + LutTableSpec::RiscvOpcodePacked { opcode, xlen } => { + if *xlen != 32 { + return Err(format!( + "RiscvOpcodePacked requires xlen=32 in shared-bus circuits (got xlen={xlen})" + )); + } + let d = neo_memory::riscv::packed::rv32_packed_d(*opcode) + .map_err(|e| format!("invalid packed opcode spec: {e}"))?; + Ok((d, 2usize)) } LutTableSpec::RiscvOpcodeEventTablePacked { .. } => { Err("RiscvOpcodeEventTablePacked is not supported in shared-bus circuits".into()) diff --git a/crates/neo-fold/src/shard/prover.rs b/crates/neo-fold/src/shard/prover.rs index 74f74fc8..ec0c339f 100644 --- a/crates/neo-fold/src/shard/prover.rs +++ b/crates/neo-fold/src/shard/prover.rs @@ -289,7 +289,8 @@ where )?; let twist_pre = - crate::memory_sidecar::memory::prove_twist_addr_pre_time(tr, params, step, &cpu_bus, ell_n, &r_cycle)?; + crate::memory_sidecar::memory::prove_twist_addr_pre_time(tr, params, step, &cpu_bus, ell_n, &r_cycle) + .map_err(|e| PiCcsError::ProtocolError(format!("twist addr-pre failed at step_idx={step_idx}: {e}")))?; let twist_read_claims: Vec = twist_pre.iter().map(|p| p.read_check_claim_sum).collect(); let twist_write_claims: Vec = twist_pre.iter().map(|p| p.write_check_claim_sum).collect(); let mut mem_oracles = crate::memory_sidecar::memory::build_route_a_memory_oracles( diff --git a/crates/neo-fold/tests/suites/integration/riscv_trace_wiring_runner_e2e.rs b/crates/neo-fold/tests/suites/integration/riscv_trace_wiring_runner_e2e.rs index 2ab92a2f..2628c287 100644 --- a/crates/neo-fold/tests/suites/integration/riscv_trace_wiring_runner_e2e.rs +++ b/crates/neo-fold/tests/suites/integration/riscv_trace_wiring_runner_e2e.rs @@ -712,6 +712,114 @@ fn prove_verify_trace_program(program: Vec) { run.verify().expect("trace wiring verify"); } +#[test] +fn rv32_trace_wiring_runner_accepts_wrapped_load_store_addressing() { + // Build base near 2^32 so effective address wraps: + // rs1 = 0xffff_ff10, imm = 0x140 => addr = 0x50 (mod 2^32). + let program = vec![ + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 2, + rs1: 0, + imm: -240, + }, + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 13, + rs1: 0, + imm: 42, + }, + RiscvInstruction::Store { + op: RiscvMemOp::Sw, + rs1: 2, + rs2: 13, + imm: 320, + }, + RiscvInstruction::Load { + op: RiscvMemOp::Lw, + rd: 14, + rs1: 2, + imm: 320, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .reg_output_claim(/*reg=*/ 14, /*expected=*/ neo_math::F::from_u64(42)) + .min_trace_len(program.len()) + .max_steps(program.len()) + .prove() + .expect("trace wiring prove with wrapped load/store address"); + run.verify() + .expect("trace wiring verify with wrapped load/store address"); +} + +#[test] +fn rv32_trace_wiring_runner_accepts_load_to_x0_without_writeback() { + // Regression for W3 load-semantics gating: loads to x0 are legal and must not + // require rd writeback while still enforcing RAM-read semantics. + let program = vec![ + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 1, + rs1: 0, + imm: 0x100, + }, + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 2, + rs1: 0, + imm: 0x7f, + }, + RiscvInstruction::Store { + op: RiscvMemOp::Sb, + rs1: 1, + rs2: 2, + imm: 0, + }, + RiscvInstruction::Load { + op: RiscvMemOp::Lbu, + rd: 0, + rs1: 1, + imm: 0, + }, + RiscvInstruction::Load { + op: RiscvMemOp::Lbu, + rd: 3, + rs1: 1, + imm: 0, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .reg_output_claim(/*reg=*/ 3, /*expected=*/ neo_math::F::from_u64(0x7f)) + .min_trace_len(program.len()) + .max_steps(program.len()) + .prove() + .expect("trace wiring prove with load-to-x0"); + run.verify().expect("trace wiring verify with load-to-x0"); +} + +#[test] +fn rv32_trace_wiring_runner_accepts_auipc_wraparound_writeback() { + // Regression for RV32 modular writeback semantics: + // place AUIPC at pc=0x1000, use imm_u=0xffff_f000 (-1 << 12), so + // rd = (0x1000 + 0xffff_f000) mod 2^32 = 0. + let mut program = vec![RiscvInstruction::Nop; 1024]; + program.push(RiscvInstruction::Auipc { rd: 1, imm: -1 }); + program.push(RiscvInstruction::Halt); + let program_bytes = encode_program(&program); + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .reg_output_claim(/*reg=*/ 1, /*expected=*/ neo_math::F::from_u64(0)) + .min_trace_len(program.len()) + .max_steps(program.len()) + .prove() + .expect("trace wiring prove with AUIPC wraparound"); + run.verify() + .expect("trace wiring verify with AUIPC wraparound"); +} + #[test] fn rv32_trace_wiring_runner_accepts_mixed_addi_andi_halt() { let program = vec![ diff --git a/crates/neo-fold/tests/suites/perf/mod.rs b/crates/neo-fold/tests/suites/perf/mod.rs index ec884b5a..44954376 100644 --- a/crates/neo-fold/tests/suites/perf/mod.rs +++ b/crates/neo-fold/tests/suites/perf/mod.rs @@ -1,5 +1,6 @@ mod memory_adversarial_tests; mod prefix_scaling; +mod riscv_add5_vs_mul5_perf; mod riscv_trace_ab_perf; mod riscv_trace_wiring_output_binding_perf; mod single_addi_metrics_nightstream; diff --git a/crates/neo-fold/tests/suites/perf/riscv_add5_vs_mul5_perf.rs b/crates/neo-fold/tests/suites/perf/riscv_add5_vs_mul5_perf.rs new file mode 100644 index 00000000..06b77932 --- /dev/null +++ b/crates/neo-fold/tests/suites/perf/riscv_add5_vs_mul5_perf.rs @@ -0,0 +1,265 @@ +#![allow(non_snake_case)] + +use std::time::Duration; + +use neo_fold::pi_ccs::FoldingMode; +use neo_fold::riscv_trace_shard::{Rv32TraceWiring, Rv32TraceWiringRun}; +use neo_math::F; +use neo_memory::riscv::lookups::{encode_program, RiscvInstruction, RiscvOpcode}; +use p3_field::PrimeCharacteristicRing; + +#[derive(Clone, Copy, Debug)] +enum Variant { + AddFiveTimes, + MulByFive, +} + +#[derive(Clone, Copy, Debug)] +struct Stats { + min: Duration, + median: Duration, + mean: Duration, + max: Duration, +} + +#[derive(Clone, Copy, Debug)] +struct VariantStats { + prove: Stats, + verify: Stats, + trace_len: usize, +} + +#[test] +#[ignore = "perf-style test: run with `cargo test -p neo-fold --release --test perf rv32_trace_perf_add5_vs_mul5_large_value -- --ignored --nocapture`"] +fn rv32_trace_perf_add5_vs_mul5_large_value() { + let repeats = env_usize("ADD5_MUL5_REPEATS", 1024); + let warmups = env_usize("ADD5_MUL5_WARMUPS", 1); + let samples = env_usize("ADD5_MUL5_SAMPLES", 7); + let large_value = env_u32("ADD5_MUL5_VALUE", 0x7F12_6789); + assert!(repeats > 0, "ADD5_MUL5_REPEATS must be > 0"); + assert!(samples > 0, "ADD5_MUL5_SAMPLES must be > 0"); + + let add_program = build_program(Variant::AddFiveTimes, repeats, large_value); + let mul_program = build_program(Variant::MulByFive, repeats, large_value); + let expected = expected_result(large_value, repeats); + + let add_stats = run_samples(&add_program, expected, warmups, samples); + let mul_stats = run_samples(&mul_program, expected, warmups, samples); + + let prove_ratio = ratio(mul_stats.prove.median, add_stats.prove.median); + let verify_ratio = ratio(mul_stats.verify.median, add_stats.verify.median); + + let add_prove_per_iter = add_stats.prove.median.as_secs_f64() / repeats as f64; + let mul_prove_per_iter = mul_stats.prove.median.as_secs_f64() / repeats as f64; + let add_prove_per_inst = add_stats.prove.median.as_secs_f64() / add_stats.trace_len as f64; + let mul_prove_per_inst = mul_stats.prove.median.as_secs_f64() / mul_stats.trace_len as f64; + + println!(); + println!("{:=<112}", ""); + println!("RV32 TRACE PERF - (ADD value) x5 vs (MUL value*5), same final accumulator"); + println!("{:=<112}", ""); + println!( + "config: value={:#010x} repeats={} warmups={} samples={}", + large_value, repeats, warmups, samples + ); + println!("expected x10 final value: {:#010x} (mod 2^32)", expected); + println!( + "trace_len: add5={} instructions, mul5={} instructions", + add_stats.trace_len, mul_stats.trace_len + ); + println!("{:-<112}", ""); + println!( + "{:>12} {:>10} {:>10} {:>10} {:>10} {:>10}", + "variant", "phase", "min", "median", "mean", "max" + ); + println!("{:-<112}", ""); + print_row("add5", "prove", add_stats.prove); + print_row("mul5", "prove", mul_stats.prove); + print_row("add5", "verify", add_stats.verify); + print_row("mul5", "verify", mul_stats.verify); + println!("{:-<112}", ""); + println!( + "median ratio (mul5/add5): prove={:.3}x verify={:.3}x", + prove_ratio, verify_ratio + ); + println!( + "prove median per iteration: add5={:.6}ms mul5={:.6}ms", + add_prove_per_iter * 1000.0, + mul_prove_per_iter * 1000.0 + ); + println!( + "prove median per instruction: add5={:.6}ms mul5={:.6}ms", + add_prove_per_inst * 1000.0, + mul_prove_per_inst * 1000.0 + ); + println!("{:-<112}", ""); + println!(); +} + +fn print_row(variant: &str, phase: &str, stats: Stats) { + println!( + "{:>12} {:>10} {:>10} {:>10} {:>10} {:>10}", + variant, + phase, + fmt_duration(stats.min), + fmt_duration(stats.median), + fmt_duration(stats.mean), + fmt_duration(stats.max), + ); +} + +fn build_program(variant: Variant, repeats: usize, value: u32) -> Vec { + let (value_hi, value_lo) = split_u32_for_lui_addi(value); + let mut program = Vec::new(); + // x5 = large value + program.push(RiscvInstruction::Lui { rd: 5, imm: value_hi }); + program.push(RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 5, + rs1: 5, + imm: value_lo, + }); + // x10 accumulator defaults to 0 at reset. + match variant { + Variant::AddFiveTimes => { + for _ in 0..repeats { + for _ in 0..5usize { + program.push(RiscvInstruction::RAlu { + op: RiscvOpcode::Add, + rd: 10, + rs1: 10, + rs2: 5, + }); + } + } + } + Variant::MulByFive => { + // x6 = 5 + program.push(RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 6, + rs1: 0, + imm: 5, + }); + for _ in 0..repeats { + // x7 = x5 * 5 + program.push(RiscvInstruction::RAlu { + op: RiscvOpcode::Mul, + rd: 7, + rs1: 5, + rs2: 6, + }); + // x10 += x7 + program.push(RiscvInstruction::RAlu { + op: RiscvOpcode::Add, + rd: 10, + rs1: 10, + rs2: 7, + }); + } + } + } + program.push(RiscvInstruction::Halt); + program +} + +fn expected_result(value: u32, repeats: usize) -> u32 { + (value as u64).wrapping_mul(5).wrapping_mul(repeats as u64) as u32 +} + +fn split_u32_for_lui_addi(value: u32) -> (i32, i32) { + // Standard RV32 materialization split: value = (hi << 12) + lo, with lo in signed 12-bit range. + let value_i64 = value as i64; + let hi = ((value_i64 + 0x800) >> 12) as i32; + let lo = (value_i64 - ((hi as i64) << 12)) as i32; + debug_assert!((-2048..=2047).contains(&lo)); + (hi, lo) +} + +fn run_samples(program: &[RiscvInstruction], expected_x10: u32, warmups: usize, samples: usize) -> VariantStats { + let program_bytes = encode_program(program); + let max_steps = program.len(); + + for _ in 0..warmups { + let mut run = run_once(&program_bytes, max_steps, expected_x10).expect("warmup prove"); + run.verify().expect("warmup verify"); + } + + let mut prove_times = Vec::with_capacity(samples); + let mut verify_times = Vec::with_capacity(samples); + for _ in 0..samples { + let mut run = run_once(&program_bytes, max_steps, expected_x10).expect("prove"); + run.verify().expect("verify"); + prove_times.push(run.prove_duration()); + verify_times.push(run.verify_duration().unwrap_or(Duration::ZERO)); + } + + VariantStats { + prove: summarize(&prove_times), + verify: summarize(&verify_times), + trace_len: max_steps, + } +} + +fn run_once( + program_bytes: &[u8], + max_steps: usize, + expected_x10: u32, +) -> Result { + Rv32TraceWiring::from_rom(/*program_base=*/ 0, program_bytes) + .xlen(32) + .mode(FoldingMode::Optimized) + .min_trace_len(max_steps) + .chunk_rows(max_steps) + .max_steps(max_steps) + .shout_auto_minimal() + .reg_output_claim(/*reg=*/ 10, F::from_u64(expected_x10 as u64)) + .prove() +} + +fn summarize(samples: &[Duration]) -> Stats { + assert!(!samples.is_empty()); + let mut v = samples.to_vec(); + v.sort_unstable(); + let min = v[0]; + let max = v[v.len() - 1]; + let median = v[v.len() / 2]; + let mean_secs = v.iter().map(Duration::as_secs_f64).sum::() / v.len() as f64; + let mean = Duration::from_secs_f64(mean_secs); + Stats { min, median, mean, max } +} + +fn ratio(numer: Duration, denom: Duration) -> f64 { + if denom.is_zero() { + return f64::INFINITY; + } + numer.as_secs_f64() / denom.as_secs_f64() +} + +fn fmt_duration(d: Duration) -> String { + if d.as_secs_f64() < 1.0 { + format!("{:.3}ms", d.as_secs_f64() * 1000.0) + } else { + format!("{:.3}s", d.as_secs_f64()) + } +} + +fn env_usize(name: &str, default: usize) -> usize { + match std::env::var(name) { + Ok(v) => v.parse::().unwrap_or(default), + Err(_) => default, + } +} + +fn env_u32(name: &str, default: u32) -> u32 { + match std::env::var(name) { + Ok(v) => { + if let Some(hex) = v.strip_prefix("0x").or_else(|| v.strip_prefix("0X")) { + u32::from_str_radix(hex, 16).unwrap_or(default) + } else { + v.parse::().unwrap_or(default) + } + } + Err(_) => default, + } +} diff --git a/crates/neo-fold/tests/suites/rv32m/riscv_rv32m_mul_divu_remu_prove_verify.rs b/crates/neo-fold/tests/suites/rv32m/riscv_rv32m_mul_divu_remu_prove_verify.rs index eb06a529..499b1560 100644 --- a/crates/neo-fold/tests/suites/rv32m/riscv_rv32m_mul_divu_remu_prove_verify.rs +++ b/crates/neo-fold/tests/suites/rv32m/riscv_rv32m_mul_divu_remu_prove_verify.rs @@ -1,3 +1,4 @@ +use neo_fold::pi_ccs::FoldingMode; use neo_fold::riscv_trace_shard::Rv32TraceWiring; use neo_memory::riscv::lookups::{encode_program, RiscvInstruction, RiscvOpcode}; use p3_field::PrimeCharacteristicRing; @@ -132,3 +133,266 @@ fn rv32_trace_prove_verify_signed_compare_path() { .expect("prove"); run.verify().expect("verify"); } + +#[test] +fn rv32_trace_prove_verify_mulhu_div_rem_paths() { + let program = vec![ + // x1 = 65536 + RiscvInstruction::Lui { rd: 1, imm: 16 }, + // x2 = 65536 + RiscvInstruction::Lui { rd: 2, imm: 16 }, + // x3 = MULHU(x1, x2) = 1 + RiscvInstruction::RAlu { + op: RiscvOpcode::Mulhu, + rd: 3, + rs1: 1, + rs2: 2, + }, + // x4 = -7 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 4, + rs1: 0, + imm: -7, + }, + // x5 = 3 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 5, + rs1: 0, + imm: 3, + }, + // x6 = DIV(x4, x5) = -2 + RiscvInstruction::RAlu { + op: RiscvOpcode::Div, + rd: 6, + rs1: 4, + rs2: 5, + }, + // x7 = REM(x4, x5) = -1 + RiscvInstruction::RAlu { + op: RiscvOpcode::Rem, + rd: 7, + rs1: 4, + rs2: 5, + }, + // x8 = 13 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 8, + rs1: 0, + imm: 13, + }, + // x9 = 0 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 9, + rs1: 0, + imm: 0, + }, + // x10 = DIV(x8, x9) => all ones + RiscvInstruction::RAlu { + op: RiscvOpcode::Div, + rd: 10, + rs1: 8, + rs2: 9, + }, + // x11 = REM(x8, x9) => dividend + RiscvInstruction::RAlu { + op: RiscvOpcode::Rem, + rd: 11, + rs1: 8, + rs2: 9, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .mode(FoldingMode::Optimized) + .chunk_rows(program.len()) + .min_trace_len(program.len()) + .max_steps(program.len()) + .reg_output_claim(/*reg=*/ 3, /*expected=*/ F::from_u64(1)) + .reg_output_claim(/*reg=*/ 6, /*expected=*/ F::from_u64(0xFFFF_FFFEu64)) + .reg_output_claim(/*reg=*/ 7, /*expected=*/ F::from_u64(0xFFFF_FFFFu64)) + .reg_output_claim(/*reg=*/ 10, /*expected=*/ F::from_u64(0xFFFF_FFFFu64)) + .reg_output_claim(/*reg=*/ 11, /*expected=*/ F::from_u64(13)) + .prove() + .expect("prove"); + run.verify().expect("verify"); +} + +#[test] +fn rv32_trace_prove_verify_mulhu_only() { + let program = vec![ + RiscvInstruction::Lui { rd: 1, imm: 16 }, + RiscvInstruction::Lui { rd: 2, imm: 16 }, + RiscvInstruction::RAlu { + op: RiscvOpcode::Mulhu, + rd: 3, + rs1: 1, + rs2: 2, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .mode(FoldingMode::Optimized) + .chunk_rows(program.len()) + .min_trace_len(program.len()) + .max_steps(program.len()) + .reg_output_claim(/*reg=*/ 3, /*expected=*/ F::from_u64(1)) + .prove() + .expect("prove"); + run.verify().expect("verify"); +} + +#[test] +fn rv32_trace_prove_verify_div_only() { + let program = vec![ + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 1, + rs1: 0, + imm: -7, + }, + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 2, + rs1: 0, + imm: 3, + }, + RiscvInstruction::RAlu { + op: RiscvOpcode::Div, + rd: 3, + rs1: 1, + rs2: 2, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .mode(FoldingMode::Optimized) + .chunk_rows(program.len()) + .min_trace_len(program.len()) + .max_steps(program.len()) + .reg_output_claim(/*reg=*/ 3, /*expected=*/ F::from_u64(0xFFFF_FFFEu64)) + .prove() + .expect("prove"); + run.verify().expect("verify"); +} + +#[test] +fn rv32_trace_prove_verify_rem_only() { + let program = vec![ + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 1, + rs1: 0, + imm: -7, + }, + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 2, + rs1: 0, + imm: 3, + }, + RiscvInstruction::RAlu { + op: RiscvOpcode::Rem, + rd: 3, + rs1: 1, + rs2: 2, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .mode(FoldingMode::Optimized) + .chunk_rows(program.len()) + .min_trace_len(program.len()) + .max_steps(program.len()) + .reg_output_claim(/*reg=*/ 3, /*expected=*/ F::from_u64(0xFFFF_FFFFu64)) + .prove() + .expect("prove"); + run.verify().expect("verify"); +} + +#[test] +fn rv32_trace_prove_verify_mulh_divu_paths() { + let program = vec![ + // x1 = -2 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 1, + rs1: 0, + imm: -2, + }, + // x2 = 3 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 2, + rs1: 0, + imm: 3, + }, + // x3 = MULH(x1, x2) = high32(signed(-2) * signed(3)) = 0xffffffff + RiscvInstruction::RAlu { + op: RiscvOpcode::Mulh, + rd: 3, + rs1: 1, + rs2: 2, + }, + // x4 = 13 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 4, + rs1: 0, + imm: 13, + }, + // x5 = 5 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 5, + rs1: 0, + imm: 5, + }, + // x6 = DIVU(x4, x5) = 2 + RiscvInstruction::RAlu { + op: RiscvOpcode::Divu, + rd: 6, + rs1: 4, + rs2: 5, + }, + // x7 = 0 + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 7, + rs1: 0, + imm: 0, + }, + // x8 = DIVU(x4, x7) = 0xffffffff (RISC-V div-by-zero behavior) + RiscvInstruction::RAlu { + op: RiscvOpcode::Divu, + rd: 8, + rs1: 4, + rs2: 7, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .mode(FoldingMode::Optimized) + .chunk_rows(program.len()) + .min_trace_len(program.len()) + .max_steps(program.len()) + .reg_output_claim(/*reg=*/ 3, /*expected=*/ F::from_u64(0xFFFF_FFFFu64)) + .reg_output_claim(/*reg=*/ 6, /*expected=*/ F::from_u64(2)) + .reg_output_claim(/*reg=*/ 8, /*expected=*/ F::from_u64(0xFFFF_FFFFu64)) + .prove() + .expect("prove"); + run.verify().expect("verify"); +} diff --git a/crates/neo-fold/tests/suites/shared_bus/mod.rs b/crates/neo-fold/tests/suites/shared_bus/mod.rs index 90343f32..b520b58a 100644 --- a/crates/neo-fold/tests/suites/shared_bus/mod.rs +++ b/crates/neo-fold/tests/suites/shared_bus/mod.rs @@ -7,6 +7,7 @@ mod shared_cpu_bus_control_attacks; mod shared_cpu_bus_decode_attacks; mod shared_cpu_bus_layout_consistency; mod shared_cpu_bus_linkage; +mod shared_cpu_bus_packed_attacks; mod shared_cpu_bus_padding_attacks; mod shared_cpu_bus_width_attacks; mod ts_route_a_negative; diff --git a/crates/neo-fold/tests/suites/shared_bus/shared_cpu_bus_packed_attacks.rs b/crates/neo-fold/tests/suites/shared_bus/shared_cpu_bus_packed_attacks.rs new file mode 100644 index 00000000..cfb5bedb --- /dev/null +++ b/crates/neo-fold/tests/suites/shared_bus/shared_cpu_bus_packed_attacks.rs @@ -0,0 +1,120 @@ +use neo_fold::pi_ccs::FoldingMode; +use neo_fold::riscv_trace_shard::{Rv32TraceWiring, Rv32TraceWiringRun}; +use neo_fold::shard::ShardProof; +use neo_math::K; +use neo_memory::cpu::bus_layout::{ + build_bus_layout_for_instances_with_shout_shapes_and_twist_lanes, ShoutInstanceShape, +}; +use neo_memory::riscv::lookups::{encode_program, RiscvInstruction, RiscvOpcode, RiscvShoutTables}; +use neo_memory::witness::LutTableSpec; +use p3_field::PrimeCharacteristicRing; + +fn prove_packed_divu_trace_program() -> (Rv32TraceWiringRun, ShardProof) { + // Program includes DIVU so shared-bus route uses packed RV32M lane columns. + let program = vec![ + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 1, + rs1: 0, + imm: 13, + }, + RiscvInstruction::IAlu { + op: RiscvOpcode::Add, + rd: 2, + rs1: 0, + imm: 5, + }, + RiscvInstruction::RAlu { + op: RiscvOpcode::Divu, + rd: 3, + rs1: 1, + rs2: 2, + }, + RiscvInstruction::Halt, + ]; + let program_bytes = encode_program(&program); + + let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes) + .mode(FoldingMode::Optimized) + .chunk_rows(program.len()) + .min_trace_len(program.len()) + .max_steps(program.len()) + .prove() + .expect("trace wiring prove"); + run.verify().expect("trace wiring verify"); + + let proof = run.proof().clone(); + (run, proof) +} + +fn tamper_divu_packed_diff_bit_opening(run: &Rv32TraceWiringRun, proof: &mut ShardProof) { + let steps = run.steps_public(); + assert_eq!(steps.len(), 1, "expected one trace step"); + let step0 = &steps[0]; + + let shout_shapes = step0.lut_insts.iter().map(|inst| ShoutInstanceShape { + ell_addr: inst.d * inst.ell, + lanes: inst.lanes.max(1), + n_vals: 1usize, + addr_group: inst.addr_group, + selector_group: inst.selector_group, + }); + let twist_shapes = step0.mem_insts.iter().map(|inst| { + assert!(inst.n_side.is_power_of_two(), "twist n_side must be power-of-two"); + let ell = inst.n_side.trailing_zeros() as usize; + (inst.d * ell, inst.lanes.max(1)) + }); + + let bus = build_bus_layout_for_instances_with_shout_shapes_and_twist_lanes( + run.layout().m, + run.layout().m_in, + run.layout().t, + shout_shapes, + twist_shapes, + ) + .expect("shared-bus layout"); + + let divu_table_id = RiscvShoutTables::new(32).opcode_to_id(RiscvOpcode::Divu).0; + let divu_inst_idx = step0 + .lut_insts + .iter() + .position(|inst| { + inst.table_id == divu_table_id + && matches!( + inst.table_spec, + Some(LutTableSpec::RiscvOpcodePacked { + opcode: RiscvOpcode::Divu, + xlen: 32 + }) + ) + }) + .expect("expected packed DIVU lookup instance"); + + let lane0 = bus.shout_cols[divu_inst_idx] + .lanes + .first() + .expect("expected lane 0 for DIVU"); + + // DIVU packed layout: [lhs, rhs, rem, rhs_inv, rhs_is_zero, diff, diff_bits(32)]. + // Flip the first diff bit (packed-only aux column). + let diff_bit0_col = lane0.addr_bits.start + 6; + + let ccs_out0 = &mut proof.steps[0].fold.ccs_out[0]; + let bus_y_base = ccs_out0 + .y_scalars + .len() + .checked_sub(bus.bus_cols) + .expect("CPU y_scalars should include shared-bus tail openings"); + let open_idx = bus.y_scalar_index(bus_y_base, diff_bit0_col); + ccs_out0.y_scalars[open_idx] += K::ONE; +} + +#[test] +fn packed_divu_diff_bit_tamper_is_rejected() { + let (run, mut proof) = prove_packed_divu_trace_program(); + tamper_divu_packed_diff_bit_opening(&run, &mut proof); + assert!( + run.verify_proof(&proof).is_err(), + "tampered packed DIVU diff-bit opening must fail verification" + ); +} diff --git a/crates/neo-memory/src/addr.rs b/crates/neo-memory/src/addr.rs index 1183962d..a315c01e 100644 --- a/crates/neo-memory/src/addr.rs +++ b/crates/neo-memory/src/addr.rs @@ -108,35 +108,6 @@ pub fn validate_pow2_bit_addressing_shape(proto: &'static str, n_side: usize, el pub fn validate_shout_bit_addressing(inst: &LutInstance) -> Result<(), PiCcsError> { // Virtual/implicit tables may not have a materialized `k = n_side^d` table. if let Some(spec) = &inst.table_spec { - let rv32_packed_expected_d = |opcode: crate::riscv::lookups::RiscvOpcode| -> Result { - Ok(match opcode { - crate::riscv::lookups::RiscvOpcode::And - | crate::riscv::lookups::RiscvOpcode::Andn - | crate::riscv::lookups::RiscvOpcode::Xor - | crate::riscv::lookups::RiscvOpcode::Or => 34usize, - crate::riscv::lookups::RiscvOpcode::Add | crate::riscv::lookups::RiscvOpcode::Sub => 3usize, - crate::riscv::lookups::RiscvOpcode::Eq | crate::riscv::lookups::RiscvOpcode::Neq => 35usize, - crate::riscv::lookups::RiscvOpcode::Slt => 37usize, - crate::riscv::lookups::RiscvOpcode::Sll => 38usize, - crate::riscv::lookups::RiscvOpcode::Srl => 38usize, - crate::riscv::lookups::RiscvOpcode::Sra => 38usize, - crate::riscv::lookups::RiscvOpcode::Sltu => 35usize, - crate::riscv::lookups::RiscvOpcode::Mul => 34usize, - crate::riscv::lookups::RiscvOpcode::Mulh => 38usize, - crate::riscv::lookups::RiscvOpcode::Mulhu => 34usize, - crate::riscv::lookups::RiscvOpcode::Mulhsu => 37usize, - crate::riscv::lookups::RiscvOpcode::Div => 43usize, - crate::riscv::lookups::RiscvOpcode::Divu => 38usize, - crate::riscv::lookups::RiscvOpcode::Rem => 43usize, - crate::riscv::lookups::RiscvOpcode::Remu => 38usize, - _ => { - return Err(PiCcsError::InvalidInput(format!( - "Shout(RISC-V packed): unsupported opcode={opcode:?}" - ))); - } - }) - }; - validate_pow2_bit_addressing_shape("Shout", inst.n_side, inst.ell)?; if inst.k != 0 { return Err(PiCcsError::InvalidInput( @@ -174,7 +145,7 @@ pub fn validate_shout_bit_addressing(inst: &LutInstance) -> Resu "Shout(RISC-V packed): expected xlen=32, got xlen={xlen}" ))); } - let expected_d = rv32_packed_expected_d(*opcode)?; + let expected_d = crate::riscv::packed::rv32_packed_d(*opcode)?; // Packed-key Shout lanes are not bit-addressed: we repurpose the addr-bit slice as // `[lhs_u32, rhs_u32, aux...]` and keep `[has_lookup, val_u32]`. if inst.n_side != 2 || inst.ell != 1 { @@ -210,7 +181,7 @@ pub fn validate_shout_bit_addressing(inst: &LutInstance) -> Resu "Shout(RISC-V event-table packed): time_bits={time_bits} too large (max 64)" ))); } - let base_d = rv32_packed_expected_d(*opcode)?; + let base_d = crate::riscv::packed::rv32_packed_d(*opcode)?; let expected_d = time_bits .checked_add(base_d) .ok_or_else(|| PiCcsError::InvalidInput("Shout(RISC-V event-table packed): d overflow".into()))?; diff --git a/crates/neo-memory/src/builder.rs b/crates/neo-memory/src/builder.rs index 7ebcc9e1..2b6d1e3f 100644 --- a/crates/neo-memory/src/builder.rs +++ b/crates/neo-memory/src/builder.rs @@ -371,10 +371,17 @@ where let ell = 1usize; (0usize, d, n_side, ell, Vec::new()) } - LutTableSpec::RiscvOpcodePacked { .. } => { - return Err(ShardBuildError::InvalidInit( - "RiscvOpcodePacked is not supported in the chunked builder path".into(), - )); + LutTableSpec::RiscvOpcodePacked { opcode, xlen } => { + if *xlen != 32 { + return Err(ShardBuildError::InvalidInit(format!( + "RiscvOpcodePacked requires xlen=32 in shared-bus mode (got xlen={xlen})" + ))); + } + let d = crate::riscv::packed::rv32_packed_d(*opcode) + .map_err(|e| ShardBuildError::InvalidInit(format!("invalid packed opcode spec: {e}")))?; + let n_side = 2usize; + let ell = 1usize; + (0usize, d, n_side, ell, Vec::new()) } LutTableSpec::RiscvOpcodeEventTablePacked { .. } => { return Err(ShardBuildError::InvalidInit( diff --git a/crates/neo-memory/src/cpu/constraints.rs b/crates/neo-memory/src/cpu/constraints.rs index ec6f7b75..568f69a6 100644 --- a/crates/neo-memory/src/cpu/constraints.rs +++ b/crates/neo-memory/src/cpu/constraints.rs @@ -44,7 +44,7 @@ use crate::cpu::bus_layout::{ build_bus_layout_for_instances_with_shout_shapes_and_twist_lanes, BusLayout, ShoutCols, ShoutInstanceShape, TwistCols, }; -use crate::witness::{LutInstance, MemInstance}; +use crate::witness::{LutInstance, LutTableSpec, MemInstance}; /// CPU column layout for binding to the bus. /// @@ -329,6 +329,7 @@ enum ShoutPaddingMode { WithoutSelectorBitness, ValueOnly, ValuePaddingOnly, + AddrPaddingOnly, AddrBitBitnessOnly, } @@ -627,6 +628,11 @@ impl CpuConstraintBuilder { self.add_shout_instance_padding_mode(layout, shout, ShoutPaddingMode::ValuePaddingOnly); } + /// Add Shout addr zero-padding only: `(1 - has_lookup) * addr_bits[i] = 0`. + pub fn add_shout_instance_addr_padding_only(&mut self, layout: &BusLayout, shout: &ShoutCols) { + self.add_shout_instance_padding_mode(layout, shout, ShoutPaddingMode::AddrPaddingOnly); + } + /// Add unconditional addr-bit booleanity constraints for one shared-address Shout group. pub fn add_shout_instance_addr_bit_bitness(&mut self, layout: &BusLayout, shout: &ShoutCols) { self.add_shout_instance_padding_mode(layout, shout, ShoutPaddingMode::AddrBitBitnessOnly); @@ -643,6 +649,7 @@ impl CpuConstraintBuilder { ); let add_gated_addr_bitness = matches!(mode, ShoutPaddingMode::Full | ShoutPaddingMode::WithoutSelectorBitness); let add_unconditional_addr_bitness = matches!(mode, ShoutPaddingMode::AddrBitBitnessOnly); + let add_addr_zero_padding = matches!(mode, ShoutPaddingMode::AddrPaddingOnly); for j in 0..layout.chunk_size { let bus_has_lookup = layout.bus_cell(shout.has_lookup, j); @@ -675,6 +682,17 @@ impl CpuConstraintBuilder { self.add_boolean_constraint(CpuConstraintLabel::ShoutAddrBitBitness, bit); } } + + if add_addr_zero_padding { + for col_id in shout.addr_bits.clone() { + let bit = layout.bus_cell(col_id, j); + self.constraints.push(CpuConstraint::new_zero_negated( + CpuConstraintLabel::LookupAddressBitsZeroPadding, + bus_has_lookup, + bit, + )); + } + } } } @@ -1076,7 +1094,13 @@ pub fn extend_ccs_with_shared_cpu_bus_constraints_optional_shout< let mut selector_bitness_added = std::collections::HashSet::::new(); let mut shout_key_binding_added = std::collections::HashSet::<(bool, usize, usize, usize, usize)>::new(); let mut shout_lane_idx = 0usize; - for inst_cols in layout.shout_cols.iter() { + for (inst_idx, inst_cols) in layout.shout_cols.iter().enumerate() { + let is_packed_opcode_lane = matches!( + lut_insts + .get(inst_idx) + .and_then(|inst| inst.table_spec.as_ref()), + Some(LutTableSpec::RiscvOpcodePacked { .. } | LutTableSpec::RiscvOpcodeEventTablePacked { .. }) + ); for lane_cols in inst_cols.lanes.iter() { let key = (lane_cols.addr_bits.start, lane_cols.addr_bits.end); let shared_addr_group = addr_range_counts.get(&key).copied().unwrap_or(0) > 1; @@ -1107,7 +1131,14 @@ pub fn extend_ccs_with_shared_cpu_bus_constraints_optional_shout< // no linkage } let selector_first = selector_bitness_added.insert(lane_cols.has_lookup); - if shared_addr_group { + if is_packed_opcode_lane { + if selector_first { + builder.add_shout_instance_padding_value_only(&layout, lane_cols); + } else { + builder.add_shout_instance_value_padding_only(&layout, lane_cols); + } + builder.add_shout_instance_addr_padding_only(&layout, lane_cols); + } else if shared_addr_group { // Shared address bits across multiple table instances cannot use per-instance // `(1-has_lookup)*addr_bit=0` gating: inactive instances would overconstrain // active ones. Enforce has/value padding per-instance and addr-bit booleanity diff --git a/crates/neo-memory/src/cpu/r1cs_adapter.rs b/crates/neo-memory/src/cpu/r1cs_adapter.rs index 144483af..9b820694 100644 --- a/crates/neo-memory/src/cpu/r1cs_adapter.rs +++ b/crates/neo-memory/src/cpu/r1cs_adapter.rs @@ -14,6 +14,8 @@ use crate::cpu::constraints::{ use crate::mem_init::MemInit; use crate::plain::LutTable; use crate::plain::PlainMemLayout; +use crate::riscv::lookups::uninterleave_bits; +use crate::riscv::packed::{build_rv32_packed_cols, rv32_packed_d}; use crate::witness::{LutInstance, LutTableSpec, MemInstance}; use neo_ajtai::{decomp_b, DecompStyle}; use neo_ccs::matrix::Mat; @@ -97,6 +99,8 @@ where /// Shout table metadata needed to write the shared CPU bus (d, n_side). pub shout_meta: HashMap, + /// Optional virtual table specs by table_id, used for packed bus witness filling. + pub shout_specs: HashMap, /// Optional shared CPU-bus configuration. /// When present, we overwrite a reserved tail segment of `z_vec` with Twist/Shout access rows @@ -125,20 +129,30 @@ where chunk_to_witness: Box]) -> Vec + Send + Sync>, ) -> Result { let mut shout_meta = HashMap::new(); + let mut shout_specs = HashMap::new(); for (id, table) in tables { shout_meta.insert(*id, (table.d, table.n_side)); } for (id, spec) in table_specs { let (d, n_side) = match spec { LutTableSpec::RiscvOpcode { xlen, .. } => (xlen.saturating_mul(2), 2usize), - LutTableSpec::RiscvOpcodePacked { .. } => { - return Err("RiscvOpcodePacked is not supported in the shared-bus R1csCpu path".into()); + LutTableSpec::RiscvOpcodePacked { opcode, xlen } => { + if *xlen != 32 { + return Err(format!( + "RiscvOpcodePacked requires xlen=32 in shared-bus R1csCpu path (got xlen={xlen})" + )); + } + ( + rv32_packed_d(*opcode).map_err(|e| format!("invalid packed opcode spec: {e}"))?, + 2usize, + ) } LutTableSpec::RiscvOpcodeEventTablePacked { .. } => { return Err("RiscvOpcodeEventTablePacked is not supported in the shared-bus R1csCpu path".into()); } LutTableSpec::IdentityU32 => (32usize, 2usize), }; + shout_specs.insert(*id, spec.clone()); match shout_meta.entry(*id) { Entry::Vacant(v) => { v.insert((d, n_side)); @@ -162,6 +176,7 @@ where committer, m_in, shout_meta, + shout_specs, shared_cpu_bus: None, chunk_to_witness, _phantom: PhantomData, @@ -404,7 +419,7 @@ where steps: chunk_size, lanes, ell, - table_spec: None, + table_spec: self.shout_specs.get(table_id).cloned(), table: Vec::new(), addr_group: cfg.shout_addr_groups.get(table_id).copied(), selector_group: cfg.shout_selector_groups.get(table_id).copied(), @@ -707,16 +722,58 @@ where for (lane_idx, shout_cols) in inst_cols.lanes.iter().enumerate() { if let Some((key, val)) = shout_events[i][lane_idx] { - write_addr_bits_dim_major_le_into_bus( - &mut z_vec, - &shared.layout, - shout_cols.addr_bits.clone(), - j, - key, - d, - n_side, - ell, - ); + match self.shout_specs.get(table_id) { + Some(LutTableSpec::RiscvOpcodePacked { opcode, xlen }) => { + if *xlen != 32 { + return Err(format!( + "packed shout table_id={table_id} requires xlen=32 (got xlen={xlen})" + )); + } + let (lhs_raw, rhs_raw) = uninterleave_bits(key as u128); + let lhs = lhs_raw as u32; + let rhs = rhs_raw as u32; + let val_u64 = val.as_canonical_u64(); + if val_u64 > u32::MAX as u64 { + return Err(format!( + "packed shout value out of u32 range for table_id={table_id} at step j={j}: val={val_u64}" + )); + } + let packed_cols = build_rv32_packed_cols::(*opcode, lhs, rhs, val_u64 as u32) + .map_err(|e| { + format!( + "packed shout column synthesis failed for table_id={table_id}, opcode={opcode:?}, step_j={j}: {e}" + ) + })?; + if packed_cols.len() != (shout_cols.addr_bits.end - shout_cols.addr_bits.start) + { + return Err(format!( + "packed shout column width mismatch for table_id={table_id}: cols_len={} bus_ell_addr={}", + packed_cols.len(), + shout_cols.addr_bits.end - shout_cols.addr_bits.start + )); + } + for (packed_idx, col_id) in shout_cols.addr_bits.clone().enumerate() { + z_vec[shared.layout.bus_cell(col_id, j)] = packed_cols[packed_idx]; + } + } + Some(LutTableSpec::RiscvOpcodeEventTablePacked { .. }) => { + return Err(format!( + "RiscvOpcodeEventTablePacked is not supported in shared-bus R1csCpu path (table_id={table_id})" + )); + } + _ => { + write_addr_bits_dim_major_le_into_bus( + &mut z_vec, + &shared.layout, + shout_cols.addr_bits.clone(), + j, + key, + d, + n_side, + ell, + ); + } + } z_vec[shared.layout.bus_cell(shout_cols.has_lookup, j)] = Goldilocks::ONE; z_vec[shared.layout.bus_cell(shout_cols.primary_val(), j)] = val; } diff --git a/crates/neo-memory/src/riscv/ccs/bus_bindings.rs b/crates/neo-memory/src/riscv/ccs/bus_bindings.rs index 28f60381..ac97b013 100644 --- a/crates/neo-memory/src/riscv/ccs/bus_bindings.rs +++ b/crates/neo-memory/src/riscv/ccs/bus_bindings.rs @@ -71,8 +71,14 @@ fn validate_trace_shout_table_id(table_id: u32) -> Result<(), String> { } #[inline] -fn trace_lookup_addr_group_for_table_id(table_id: u32) -> Option { - rv32_trace_lookup_addr_group_for_table_id(table_id) +fn trace_lookup_addr_group_for_table_id(table_id: u32, ell_addr: usize) -> Option { + // Canonical opcode addr-sharing only applies to implicit interleaved keys (ell_addr = 64 in RV32). + // Packed opcode layouts use opcode-local widths and must not share this 64-bit group. + if table_id <= REMU_TABLE_ID && ell_addr != 2 * RV32_XLEN { + None + } else { + rv32_trace_lookup_addr_group_for_table_id(table_id) + } } #[inline] @@ -96,14 +102,24 @@ fn derive_trace_shout_shapes( let mut shape_by_table_id = HashMap::::new(); for &table_id in shout_table_ids { - validate_trace_shout_table_id(table_id)?; + if let Err(err) = validate_trace_shout_table_id(table_id) { + // Allow caller-provided lookup families when an explicit extra spec defines geometry. + // In that case, defer shape creation to the extra_shout_specs pass below. + if extra_shout_specs + .iter() + .any(|spec| spec.table_id == table_id) + { + continue; + } + return Err(err); + } shape_by_table_id.insert( table_id, TraceShoutShape { table_id, ell_addr: 2 * RV32_XLEN, n_vals: 1usize, - addr_group: trace_lookup_addr_group_for_table_id(table_id), + addr_group: trace_lookup_addr_group_for_table_id(table_id, 2 * RV32_XLEN), selector_group: trace_lookup_selector_group_for_table_id(table_id), }, ); @@ -135,7 +151,7 @@ fn derive_trace_shout_shapes( spec.table_id, prev.n_vals, spec.n_vals )); } - let inferred_group = trace_lookup_addr_group_for_table_id(spec.table_id); + let inferred_group = trace_lookup_addr_group_for_table_id(spec.table_id, spec.ell_addr); if prev.addr_group != inferred_group { return Err(format!( "RV32 trace shared bus: conflicting addr_group for table_id={} (base/spec mismatch: {:?} vs {:?})", @@ -156,7 +172,7 @@ fn derive_trace_shout_shapes( table_id: spec.table_id, ell_addr: spec.ell_addr, n_vals: spec.n_vals, - addr_group: trace_lookup_addr_group_for_table_id(spec.table_id), + addr_group: trace_lookup_addr_group_for_table_id(spec.table_id, spec.ell_addr), selector_group: trace_lookup_selector_group_for_table_id(spec.table_id), }, ); @@ -602,6 +618,7 @@ pub fn rv32_trace_shared_bus_extraction_with_specs( let mut shout_key_binding_added = HashSet::<(bool, usize, usize, usize, usize)>::new(); for (i, shape) in shout_shapes.iter().enumerate() { let lane0 = &bus.shout_cols[i].lanes[0]; + let is_packed_opcode_lane = shape.table_id <= REMU_TABLE_ID && shape.ell_addr != 2 * RV32_XLEN; if let Some(binding) = trace_shout_binding(layout, shape.table_id) { let mut dedup_binding = binding.clone(); if let Some(addr_base) = dedup_binding.addr { @@ -627,7 +644,15 @@ pub fn rv32_trace_shared_bus_extraction_with_specs( let key = (lane0.addr_bits.start, lane0.addr_bits.end); let shared_addr_group = addr_range_counts.get(&key).copied().unwrap_or(0) > 1; let selector_first = selector_bitness_added.insert(lane0.has_lookup); - if shared_addr_group { + if is_packed_opcode_lane { + // Packed opcode lanes carry mixed field elements in addr_bits; addr-bit booleanity does not apply. + if selector_first { + builder.add_shout_instance_padding_value_only(&bus, lane0); + } else { + builder.add_shout_instance_value_padding_only(&bus, lane0); + } + builder.add_shout_instance_addr_padding_only(&bus, lane0); + } else if shared_addr_group { if selector_first { builder.add_shout_instance_padding_value_only(&bus, lane0); } else { diff --git a/crates/neo-memory/src/riscv/lookups/cpu.rs b/crates/neo-memory/src/riscv/lookups/cpu.rs index 6c315d7f..5f4c5e5f 100644 --- a/crates/neo-memory/src/riscv/lookups/cpu.rs +++ b/crates/neo-memory/src/riscv/lookups/cpu.rs @@ -200,80 +200,11 @@ impl neo_vm_trace::VmCpu for RiscvCpu { | RiscvOpcode::Remu if self.xlen == 32 => { - let rs1_u32 = rs1_val as u32; - let rs2_u32 = rs2_val as u32; - let rs1_i32 = rs1_u32 as i32; - let rs2_i32 = rs2_u32 as i32; - - match op { - RiscvOpcode::Mul => { - let result = rs1_u32.wrapping_mul(rs2_u32) as u64; - self.write_reg(twist, rd, result); - } - RiscvOpcode::Mulh => { - let product = (rs1_i32 as i64) * (rs2_i32 as i64); - let result = (product >> 32) as i32 as u32; - self.write_reg(twist, rd, result as u64); - } - RiscvOpcode::Mulhu => { - let product = (rs1_u32 as u64) * (rs2_u32 as u64); - let result = (product >> 32) as u32; - self.write_reg(twist, rd, result as u64); - } - RiscvOpcode::Mulhsu => { - let product = (rs1_i32 as i64) * (rs2_u32 as i64); - let result = (product >> 32) as i32 as u32; - self.write_reg(twist, rd, result as u64); - } - RiscvOpcode::Div | RiscvOpcode::Rem => { - let divisor_is_zero = rs2_u32 == 0; - let (quot_i32, rem_i32) = if divisor_is_zero { - (-1i32, rs1_i32) - } else if rs1_i32 == i32::MIN && rs2_i32 == -1 { - (rs1_i32, 0) - } else { - (rs1_i32 / rs2_i32, rs1_i32 % rs2_i32) - }; - let result = match op { - RiscvOpcode::Div => quot_i32 as u32, - RiscvOpcode::Rem => rem_i32 as u32, - _ => unreachable!(), - }; - self.write_reg(twist, rd, result as u64); - - // Record a single Shout event for the remainder bound, only when divisor != 0. - if !divisor_is_zero { - let rem_abs = (rem_i32 as i64).abs() as u64; - let divisor_abs = (rs2_i32 as i64).abs() as u64; - let sltu_id = shout_tables.opcode_to_id(RiscvOpcode::Sltu); - let index = interleave_bits(rem_abs, divisor_abs) as u64; - let _ = shout.lookup(sltu_id, index); - } - } - RiscvOpcode::Divu | RiscvOpcode::Remu => { - let dividend = rs1_u32 as u64; - let divisor = rs2_u32 as u64; - let (quot, rem) = if divisor == 0 { - (u32::MAX as u64, dividend) - } else { - (dividend / divisor, dividend % divisor) - }; - let result = match op { - RiscvOpcode::Divu => quot, - RiscvOpcode::Remu => rem, - _ => unreachable!(), - }; - self.write_reg(twist, rd, result); - - // Record a single Shout event for the remainder bound, only when divisor != 0. - if divisor != 0 { - let sltu_id = shout_tables.opcode_to_id(RiscvOpcode::Sltu); - let index = interleave_bits(rem, divisor) as u64; - let _ = shout.lookup(sltu_id, index); - } - } - _ => {} - } + // Use Shout so RV32M rows emit canonical ShoutEvent metadata for trace-side linkage. + let shout_id = shout_tables.opcode_to_id(op); + let index = interleave_bits(rs1_val, rs2_val) as u64; + let result = shout.lookup(shout_id, index); + self.write_reg(twist, rd, result); } _ => { // Use Shout for the ALU operation diff --git a/crates/neo-memory/src/riscv/lookups/decode.rs b/crates/neo-memory/src/riscv/lookups/decode.rs index 303a4feb..b5e2878c 100644 --- a/crates/neo-memory/src/riscv/lookups/decode.rs +++ b/crates/neo-memory/src/riscv/lookups/decode.rs @@ -190,15 +190,29 @@ pub fn decode_instruction(instr: u32) -> Result { } } - // MISC-MEM (0001111) - FENCE (FENCE.I unsupported) - 0b0001111 => { - if funct3 != 0b000 { - return Err(format!("Unsupported MISC-MEM instruction: funct3={:#x}", funct3)); + // MISC-MEM (0001111) - FENCE / FENCE.I + 0b0001111 => match funct3 { + // FENCE: rd/rs1 are reserved and must be x0. + 0b000 => { + if rd != 0 || rs1 != 0 { + return Err(format!( + "Invalid FENCE encoding: rd/rs1 must be x0 (rd={}, rs1={}, instr={:#x})", + rd, rs1, instr + )); + } + let pred = ((instr >> 24) & 0xF) as u8; + let succ = ((instr >> 20) & 0xF) as u8; + Ok(RiscvInstruction::Fence { pred, succ }) } - let pred = ((instr >> 24) & 0xF) as u8; - let succ = ((instr >> 20) & 0xF) as u8; - Ok(RiscvInstruction::Fence { pred, succ }) - } + // FENCE.I: imm[11:0], rd and rs1 are reserved and must be zero. + 0b001 => { + if rd != 0 || rs1 != 0 || (instr >> 20) != 0 { + return Err(format!("Invalid FENCE.I encoding: instr={:#x}", instr)); + } + Ok(RiscvInstruction::FenceI) + } + _ => Err(format!("Unsupported MISC-MEM instruction: funct3={:#x}", funct3)), + }, // OP-32 (0111011) - RV64 W-suffix R-type operations 0b0111011 => { diff --git a/crates/neo-memory/src/riscv/lookups/memory.rs b/crates/neo-memory/src/riscv/lookups/memory.rs index 32d2e41c..e8c536b0 100644 --- a/crates/neo-memory/src/riscv/lookups/memory.rs +++ b/crates/neo-memory/src/riscv/lookups/memory.rs @@ -137,6 +137,15 @@ impl Twist for RiscvMemory { return self.regs.get(idx).copied().unwrap_or(0); } + if twist_id == super::RAM_ID { + // RAM sidecar/proofs model one XLEN-wide cell per logical address. + // Map logical address `addr` to a disjoint byte range in backing storage + // so adjacent logical addresses do not overlap. + let width = self.xlen / 8; + let phys = addr.wrapping_mul(width as u64); + return self.read(twist_id, phys, width); + } + let width = if twist_id == super::PROG_ID { // Program ROM fetch: always 32-bit instruction word (MVP: no compressed). 4 @@ -161,6 +170,13 @@ impl Twist for RiscvMemory { return; } + if twist_id == super::RAM_ID { + let width = self.xlen / 8; + let phys = addr.wrapping_mul(width as u64); + self.write(twist_id, phys, width, value); + return; + } + let width = if twist_id == super::PROG_ID { 4 } else { self.xlen / 8 }; self.write(twist_id, addr, width, value); } diff --git a/crates/neo-memory/src/riscv/mod.rs b/crates/neo-memory/src/riscv/mod.rs index a3307a15..e5bf0b43 100644 --- a/crates/neo-memory/src/riscv/mod.rs +++ b/crates/neo-memory/src/riscv/mod.rs @@ -6,6 +6,7 @@ pub mod ccs; pub mod elf_loader; pub mod exec_table; pub mod lookups; +pub mod packed; pub mod rom_init; pub mod shout_oracle; pub mod sparse_access; diff --git a/crates/neo-memory/src/riscv/packed.rs b/crates/neo-memory/src/riscv/packed.rs new file mode 100644 index 00000000..ebc8c23a --- /dev/null +++ b/crates/neo-memory/src/riscv/packed.rs @@ -0,0 +1,456 @@ +use neo_reductions::error::PiCcsError; +use p3_field::{Field, PrimeCharacteristicRing}; + +use crate::riscv::lookups::{compute_op, RiscvOpcode}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PackedBitnessRole { + HasLookup, + Val, + PackedCol(usize), +} + +#[inline] +pub fn rv32_packed_supported_opcode(op: RiscvOpcode) -> bool { + matches!( + op, + RiscvOpcode::And + | RiscvOpcode::Andn + | RiscvOpcode::Or + | RiscvOpcode::Xor + | RiscvOpcode::Add + | RiscvOpcode::Sub + | RiscvOpcode::Eq + | RiscvOpcode::Neq + | RiscvOpcode::Slt + | RiscvOpcode::Sll + | RiscvOpcode::Srl + | RiscvOpcode::Sra + | RiscvOpcode::Sltu + | RiscvOpcode::Mul + | RiscvOpcode::Mulh + | RiscvOpcode::Mulhu + | RiscvOpcode::Mulhsu + | RiscvOpcode::Div + | RiscvOpcode::Divu + | RiscvOpcode::Rem + | RiscvOpcode::Remu + ) +} + +#[inline] +pub fn rv32_packed_rollout_opcode(op: RiscvOpcode) -> bool { + matches!( + op, + RiscvOpcode::Mul + | RiscvOpcode::Mulh + | RiscvOpcode::Mulhu + | RiscvOpcode::Mulhsu + | RiscvOpcode::Div + | RiscvOpcode::Divu + | RiscvOpcode::Rem + | RiscvOpcode::Remu + ) +} + +pub fn rv32_packed_d(op: RiscvOpcode) -> Result { + Ok(match op { + RiscvOpcode::And | RiscvOpcode::Andn | RiscvOpcode::Or | RiscvOpcode::Xor => 34usize, + RiscvOpcode::Add | RiscvOpcode::Sub => 3usize, + RiscvOpcode::Eq | RiscvOpcode::Neq => 35usize, + RiscvOpcode::Slt => 37usize, + RiscvOpcode::Sll | RiscvOpcode::Srl | RiscvOpcode::Sra => 38usize, + RiscvOpcode::Sltu => 35usize, + RiscvOpcode::Mul => 34usize, + RiscvOpcode::Mulh => 38usize, + RiscvOpcode::Mulhu => 34usize, + RiscvOpcode::Mulhsu => 37usize, + RiscvOpcode::Div | RiscvOpcode::Rem => 43usize, + RiscvOpcode::Divu | RiscvOpcode::Remu => 38usize, + _ => { + return Err(PiCcsError::InvalidInput(format!( + "packed RV32 opcode is unsupported: opcode={op:?}" + ))); + } + }) +} + +fn push_col_range(out: &mut Vec, start: usize, len: usize) { + for idx in start..start + len { + out.push(PackedBitnessRole::PackedCol(idx)); + } +} + +pub fn rv32_packed_bitness_roles(op: RiscvOpcode) -> Result, PiCcsError> { + let mut out = Vec::new(); + match op { + RiscvOpcode::And | RiscvOpcode::Andn | RiscvOpcode::Or | RiscvOpcode::Xor => { + out.push(PackedBitnessRole::HasLookup); + } + RiscvOpcode::Add | RiscvOpcode::Sub => { + out.push(PackedBitnessRole::PackedCol(2)); + out.push(PackedBitnessRole::HasLookup); + } + RiscvOpcode::Eq | RiscvOpcode::Neq => { + out.push(PackedBitnessRole::HasLookup); + out.push(PackedBitnessRole::Val); + out.push(PackedBitnessRole::PackedCol(2)); + push_col_range(&mut out, 3, 32); + } + RiscvOpcode::Mul | RiscvOpcode::Mulhu => { + out.push(PackedBitnessRole::HasLookup); + push_col_range(&mut out, 2, 32); + } + RiscvOpcode::Mulh => { + out.push(PackedBitnessRole::HasLookup); + out.push(PackedBitnessRole::PackedCol(3)); + out.push(PackedBitnessRole::PackedCol(4)); + push_col_range(&mut out, 6, 32); + } + RiscvOpcode::Mulhsu => { + out.push(PackedBitnessRole::HasLookup); + out.push(PackedBitnessRole::PackedCol(3)); + out.push(PackedBitnessRole::PackedCol(4)); + push_col_range(&mut out, 5, 32); + } + RiscvOpcode::Slt => { + out.push(PackedBitnessRole::Val); + out.push(PackedBitnessRole::HasLookup); + out.push(PackedBitnessRole::PackedCol(3)); + out.push(PackedBitnessRole::PackedCol(4)); + push_col_range(&mut out, 5, 32); + } + RiscvOpcode::Sll | RiscvOpcode::Srl => { + out.push(PackedBitnessRole::HasLookup); + push_col_range(&mut out, 1, 5); + push_col_range(&mut out, 6, 32); + } + RiscvOpcode::Sra => { + out.push(PackedBitnessRole::HasLookup); + push_col_range(&mut out, 1, 5); + out.push(PackedBitnessRole::PackedCol(6)); + push_col_range(&mut out, 7, 31); + } + RiscvOpcode::Sltu => { + out.push(PackedBitnessRole::Val); + out.push(PackedBitnessRole::HasLookup); + push_col_range(&mut out, 3, 32); + } + RiscvOpcode::Divu | RiscvOpcode::Remu => { + out.push(PackedBitnessRole::HasLookup); + out.push(PackedBitnessRole::PackedCol(4)); + push_col_range(&mut out, 6, 32); + } + RiscvOpcode::Div | RiscvOpcode::Rem => { + out.push(PackedBitnessRole::HasLookup); + out.push(PackedBitnessRole::PackedCol(5)); + out.push(PackedBitnessRole::PackedCol(6)); + out.push(PackedBitnessRole::PackedCol(7)); + out.push(PackedBitnessRole::PackedCol(9)); + push_col_range(&mut out, 11, 32); + } + _ => { + return Err(PiCcsError::InvalidInput(format!( + "packed RV32 bitness roles are unsupported: opcode={op:?}" + ))); + } + } + Ok(out) +} + +pub fn rv32_collect_packed_bitness_terms( + op: RiscvOpcode, + packed_cols: &[T], + has_lookup: T, + val: T, +) -> Result, PiCcsError> { + let roles = rv32_packed_bitness_roles(op)?; + let mut out = Vec::with_capacity(roles.len()); + for role in roles { + match role { + PackedBitnessRole::HasLookup => out.push(has_lookup.clone()), + PackedBitnessRole::Val => out.push(val.clone()), + PackedBitnessRole::PackedCol(idx) => { + let col = packed_cols.get(idx).ok_or_else(|| { + PiCcsError::InvalidInput(format!( + "packed RV32 bitness role index out of bounds for opcode={op:?}: idx={idx}, packed_cols={}", + packed_cols.len() + )) + })?; + out.push(col.clone()); + } + } + } + Ok(out) +} + +#[inline] +fn f_bool(bit: bool) -> F { + if bit { + F::ONE + } else { + F::ZERO + } +} + +pub fn build_rv32_packed_cols( + op: RiscvOpcode, + lhs: u32, + rhs: u32, + val: u32, +) -> Result, PiCcsError> { + if !rv32_packed_rollout_opcode(op) { + return Err(PiCcsError::InvalidInput(format!( + "packed RV32 col synthesis is unsupported for opcode={op:?}" + ))); + } + let expected = compute_op(op, lhs as u64, rhs as u64, 32) as u32; + if val != expected { + return Err(PiCcsError::InvalidInput(format!( + "packed RV32 col synthesis value mismatch for opcode={op:?}: got={val:#x}, expected={expected:#x}" + ))); + } + + match op { + RiscvOpcode::Mul => { + let wide = (lhs as u64) * (rhs as u64); + let hi = (wide >> 32) as u32; + let mut packed = Vec::with_capacity(34); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + for bit in 0..32usize { + packed.push(f_bool::(((hi >> bit) & 1) == 1)); + } + Ok(packed) + } + RiscvOpcode::Mulhu => { + let wide = (lhs as u64) * (rhs as u64); + let lo = (wide & 0xffff_ffff) as u32; + let mut packed = Vec::with_capacity(34); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + for bit in 0..32usize { + packed.push(f_bool::(((lo >> bit) & 1) == 1)); + } + Ok(packed) + } + RiscvOpcode::Mulh => { + let uprod = (lhs as u64) * (rhs as u64); + let lo = (uprod & 0xffff_ffff) as u32; + let hi = (uprod >> 32) as u32; + let lhs_sign = (lhs >> 31) & 1; + let rhs_sign = (rhs >> 31) & 1; + + let diff = + (val as i128) - (hi as i128) + (lhs_sign as i128) * (rhs as i128) + (rhs_sign as i128) * (lhs as i128); + let two32 = 1_i128 << 32; + if diff < 0 || diff % two32 != 0 { + return Err(PiCcsError::InvalidInput(format!( + "packed MULH helper: invalid k decomposition (diff={diff})" + ))); + } + let k = (diff / two32) as u32; + if k > 2 { + return Err(PiCcsError::InvalidInput(format!( + "packed MULH helper: k out of range (k={k})" + ))); + } + + let mut packed = Vec::with_capacity(38); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + packed.push(F::from_u64(hi as u64)); + packed.push(f_bool::(lhs_sign == 1)); + packed.push(f_bool::(rhs_sign == 1)); + packed.push(F::from_u64(k as u64)); + for bit in 0..32usize { + packed.push(f_bool::(((lo >> bit) & 1) == 1)); + } + Ok(packed) + } + RiscvOpcode::Mulhsu => { + let uprod = (lhs as u64) * (rhs as u64); + let lo = (uprod & 0xffff_ffff) as u32; + let hi = (uprod >> 32) as u32; + let lhs_sign = (lhs >> 31) & 1; + + let diff = (val as i128) - (hi as i128) + (lhs_sign as i128) * (rhs as i128); + let two32 = 1_i128 << 32; + if diff < 0 || diff % two32 != 0 { + return Err(PiCcsError::InvalidInput(format!( + "packed MULHSU helper: invalid borrow decomposition (diff={diff})" + ))); + } + let borrow = (diff / two32) as u32; + if borrow > 1 { + return Err(PiCcsError::InvalidInput(format!( + "packed MULHSU helper: borrow out of range (borrow={borrow})" + ))); + } + + let mut packed = Vec::with_capacity(37); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + packed.push(F::from_u64(hi as u64)); + packed.push(f_bool::(lhs_sign == 1)); + packed.push(f_bool::(borrow == 1)); + for bit in 0..32usize { + packed.push(f_bool::(((lo >> bit) & 1) == 1)); + } + Ok(packed) + } + RiscvOpcode::Div => { + let lhs_sign = (lhs >> 31) & 1; + let rhs_sign = (rhs >> 31) & 1; + let lhs_abs = if lhs_sign == 0 { lhs } else { lhs.wrapping_neg() }; + let rhs_abs = if rhs == 0 { + 0u32 + } else if rhs_sign == 0 { + rhs + } else { + rhs.wrapping_neg() + }; + + let rhs_f = F::from_u64(rhs as u64); + let rhs_inv = if rhs_f == F::ZERO { F::ZERO } else { rhs_f.inverse() }; + let rhs_is_zero = rhs == 0; + + let (q_abs, r_abs) = if rhs == 0 { + (0u32, 0u32) + } else { + (lhs_abs / rhs_abs, lhs_abs % rhs_abs) + }; + let q_is_zero = q_abs == 0; + let q_f = F::from_u64(q_abs as u64); + let q_inv = if q_f == F::ZERO { F::ZERO } else { q_f.inverse() }; + let diff = if rhs == 0 { 0u32 } else { r_abs.wrapping_sub(rhs_abs) }; + + let mut packed = Vec::with_capacity(43); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + packed.push(F::from_u64(q_abs as u64)); + packed.push(F::from_u64(r_abs as u64)); + packed.push(rhs_inv); + packed.push(f_bool::(rhs_is_zero)); + packed.push(f_bool::(lhs_sign == 1)); + packed.push(f_bool::(rhs_sign == 1)); + packed.push(q_inv); + packed.push(f_bool::(q_is_zero)); + packed.push(F::from_u64(diff as u64)); + for bit in 0..32usize { + packed.push(f_bool::(((diff >> bit) & 1) == 1)); + } + Ok(packed) + } + RiscvOpcode::Divu => { + let rhs_f = F::from_u64(rhs as u64); + let rhs_inv = if rhs_f == F::ZERO { F::ZERO } else { rhs_f.inverse() }; + let rhs_is_zero = rhs == 0; + + let rem = if rhs == 0 { + 0u32 + } else { + ((lhs as u64) % (rhs as u64)) as u32 + }; + if rhs != 0 { + let rem_check = (lhs as u64).wrapping_sub((rhs as u64).wrapping_mul(val as u64)) as u32; + if rem_check != rem { + return Err(PiCcsError::InvalidInput(format!( + "packed DIVU helper: invalid quotient/remainder relation (rem_check={rem_check:#x}, rem={rem:#x})" + ))); + } + } + let diff = rem.wrapping_sub(rhs); + + let mut packed = Vec::with_capacity(38); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + packed.push(F::from_u64(rem as u64)); + packed.push(rhs_inv); + packed.push(f_bool::(rhs_is_zero)); + packed.push(F::from_u64(diff as u64)); + for bit in 0..32usize { + packed.push(f_bool::(((diff >> bit) & 1) == 1)); + } + Ok(packed) + } + RiscvOpcode::Rem => { + let lhs_sign = (lhs >> 31) & 1; + let rhs_sign = (rhs >> 31) & 1; + let lhs_abs = if lhs_sign == 0 { lhs } else { lhs.wrapping_neg() }; + let rhs_abs = if rhs == 0 { + 0u32 + } else if rhs_sign == 0 { + rhs + } else { + rhs.wrapping_neg() + }; + + let rhs_f = F::from_u64(rhs as u64); + let rhs_inv = if rhs_f == F::ZERO { F::ZERO } else { rhs_f.inverse() }; + let rhs_is_zero = rhs == 0; + + let (q_abs, r_abs) = if rhs == 0 { + (0u32, 0u32) + } else { + (lhs_abs / rhs_abs, lhs_abs % rhs_abs) + }; + let r_is_zero = r_abs == 0; + let r_f = F::from_u64(r_abs as u64); + let r_inv = if r_f == F::ZERO { F::ZERO } else { r_f.inverse() }; + let diff = if rhs == 0 { 0u32 } else { r_abs.wrapping_sub(rhs_abs) }; + + let mut packed = Vec::with_capacity(43); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + packed.push(F::from_u64(q_abs as u64)); + packed.push(F::from_u64(r_abs as u64)); + packed.push(rhs_inv); + packed.push(f_bool::(rhs_is_zero)); + packed.push(f_bool::(lhs_sign == 1)); + packed.push(f_bool::(rhs_sign == 1)); + packed.push(r_inv); + packed.push(f_bool::(r_is_zero)); + packed.push(F::from_u64(diff as u64)); + for bit in 0..32usize { + packed.push(f_bool::(((diff >> bit) & 1) == 1)); + } + Ok(packed) + } + RiscvOpcode::Remu => { + let rhs_f = F::from_u64(rhs as u64); + let rhs_inv = if rhs_f == F::ZERO { F::ZERO } else { rhs_f.inverse() }; + let rhs_is_zero = rhs == 0; + + let quot = if rhs == 0 { + 0u32 + } else { + (lhs as u64 / rhs as u64) as u32 + }; + if rhs != 0 { + let rem_check = ((lhs as u64) % (rhs as u64)) as u32; + if rem_check != val { + return Err(PiCcsError::InvalidInput(format!( + "packed REMU helper: invalid remainder relation (rem_check={rem_check:#x}, val={val:#x})" + ))); + } + } + let diff = val.wrapping_sub(rhs); + + let mut packed = Vec::with_capacity(38); + packed.push(F::from_u64(lhs as u64)); + packed.push(F::from_u64(rhs as u64)); + packed.push(F::from_u64(quot as u64)); + packed.push(rhs_inv); + packed.push(f_bool::(rhs_is_zero)); + packed.push(F::from_u64(diff as u64)); + for bit in 0..32usize { + packed.push(f_bool::(((diff >> bit) & 1) == 1)); + } + Ok(packed) + } + _ => Err(PiCcsError::InvalidInput(format!( + "packed RV32 col synthesis is unsupported for opcode={op:?}" + ))), + } +} diff --git a/crates/neo-memory/src/riscv/trace/decode_lookup.rs b/crates/neo-memory/src/riscv/trace/decode_lookup.rs index e584a8bd..7b964779 100644 --- a/crates/neo-memory/src/riscv/trace/decode_lookup.rs +++ b/crates/neo-memory/src/riscv/trace/decode_lookup.rs @@ -417,6 +417,7 @@ pub fn rv32_decode_lookup_backed_row_from_instr_word( } let funct7_b5 = (funct7_u64 >> 5) & 1; + let is_rv32m = (funct7_u64 & 1) == 1; let f3_is_0 = if active && funct3_u64 == 0 { 1 } else { 0 }; let f3_is_5 = if active && funct3_u64 == 5 { 1 } else { 0 }; let alu_table_base: u64 = match funct3_u64 { @@ -429,10 +430,26 @@ pub fn rv32_decode_lookup_backed_row_from_instr_word( 6 => 2, _ => 0, }; + let alu_rv32m_table: u64 = match funct3_u64 { + 0 => 12, // MUL + 1 => 13, // MULH + 2 => 15, // MULHSU + 3 => 14, // MULHU + 4 => 16, // DIV + 5 => 17, // DIVU + 6 => 18, // REM + 7 => 19, // REMU + _ => 0, + }; + let alu_reg_table_id = if is_rv32m { + alu_rv32m_table + } else { + alu_table_base + (funct7_b5 * (f3_is_0 + f3_is_5)) + }; let branch_table_expected: u64 = 10 - 5 * ((funct3_u64 >> 2) & 1) + (((funct3_u64 >> 1) & 1) * ((funct3_u64 >> 2) & 1)); row[layout.shout_table_id] = if opcode_u64 == 0x33 { - F::from_u64(alu_table_base + (funct7_b5 * (f3_is_0 + f3_is_5))) + F::from_u64(alu_reg_table_id) } else if opcode_u64 == 0x13 { F::from_u64(alu_table_base + (funct7_b5 * f3_is_5)) } else if opcode_u64 == 0x63 { @@ -443,7 +460,7 @@ pub fn rv32_decode_lookup_backed_row_from_instr_word( } else { F::ZERO }; - row[layout.alu_reg_table_delta] = F::from_u64(funct7_b5 * (f3_is_0 + f3_is_5)); + row[layout.alu_reg_table_delta] = F::from_u64(alu_reg_table_id.wrapping_sub(alu_table_base)); row[layout.alu_imm_table_delta] = F::from_u64(funct7_b5 * f3_is_5); let shift_f3_sel = row[layout.funct3_is[1]] + row[layout.funct3_is[5]]; diff --git a/crates/neo-memory/src/twist_oracle.rs b/crates/neo-memory/src/twist_oracle.rs index 086dfb29..632557fc 100644 --- a/crates/neo-memory/src/twist_oracle.rs +++ b/crates/neo-memory/src/twist_oracle.rs @@ -1,4 +1,3 @@ - use crate::bit_ops::eq_bit_affine; use crate::mle::{eq_single, lt_eval}; use crate::sparse_time::SparseIdxVec; @@ -1805,8 +1804,7 @@ fn accumulate_pair_with_eq_addr_over_points( eq0s_scratch: &mut Vec, d_eqs_scratch: &mut Vec, mut coeff_at: F, -) -where +) where F: FnMut(K) -> K, { debug_assert_eq!(bit_cols.len(), r_addr.len()); @@ -2235,7 +2233,15 @@ impl TwistReadCheckOracleSparseTime { let inc_terms_at_r_addr = build_inc_terms_at_r_addr(&wa_bits, &has_write, &inc_at_write_addr, r_addr); - Self::new_with_inc_terms(r_cycle, has_read, rv, ra_bits, r_addr, init_at_r_addr, inc_terms_at_r_addr) + Self::new_with_inc_terms( + r_cycle, + has_read, + rv, + ra_bits, + r_addr, + init_at_r_addr, + inc_terms_at_r_addr, + ) } pub fn new_with_inc_terms( @@ -2998,13 +3004,8 @@ impl TwistWriteCheckAddrOracleSparseTime { wa_bits: &[SparseIdxVec], inc_at_write_addr: SparseIdxVec, ) -> Self { - let (ell_addr, events) = collect_singlelane_write_addr_events( - r_cycle, - &has_write, - &wv, - wa_bits, - &inc_at_write_addr, - ); + let (ell_addr, events) = + collect_singlelane_write_addr_events(r_cycle, &has_write, &wv, wa_bits, &inc_at_write_addr); assert_init_sparse_in_range(&init_sparse, ell_addr); Self { diff --git a/crates/neo-memory/tests/riscv_decode_lookup_delta_consistency.rs b/crates/neo-memory/tests/riscv_decode_lookup_delta_consistency.rs new file mode 100644 index 00000000..9a93596c --- /dev/null +++ b/crates/neo-memory/tests/riscv_decode_lookup_delta_consistency.rs @@ -0,0 +1,65 @@ +use neo_memory::riscv::lookups::{RiscvOpcode, RiscvShoutTables}; +use neo_memory::riscv::trace::{rv32_decode_lookup_backed_row_from_instr_word, Rv32DecodeSidecarLayout}; +use p3_field::PrimeCharacteristicRing; +use p3_goldilocks::Goldilocks as F; + +fn encode_r_type(funct7: u32, funct3: u32, rd: u32, rs1: u32, rs2: u32) -> u32 { + (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | 0b0110011 +} + +fn base_alu_opcode_for_funct3(funct3: u32) -> RiscvOpcode { + match funct3 { + 0 => RiscvOpcode::Add, + 1 => RiscvOpcode::Sll, + 2 => RiscvOpcode::Slt, + 3 => RiscvOpcode::Sltu, + 4 => RiscvOpcode::Xor, + 5 => RiscvOpcode::Srl, + 6 => RiscvOpcode::Or, + 7 => RiscvOpcode::And, + _ => unreachable!("funct3 must be in 0..8"), + } +} + +fn rv32m_opcode_for_funct3(funct3: u32) -> RiscvOpcode { + match funct3 { + 0 => RiscvOpcode::Mul, + 1 => RiscvOpcode::Mulh, + 2 => RiscvOpcode::Mulhsu, + 3 => RiscvOpcode::Mulhu, + 4 => RiscvOpcode::Div, + 5 => RiscvOpcode::Divu, + 6 => RiscvOpcode::Rem, + 7 => RiscvOpcode::Remu, + _ => unreachable!("funct3 must be in 0..8"), + } +} + +#[test] +fn rv32_decode_lookup_rv32m_alu_delta_matches_canonical_table_id_mapping() { + let layout = Rv32DecodeSidecarLayout::new(); + let shout = RiscvShoutTables::new(32); + + for funct3 in 0u32..8u32 { + // OP (R-type), funct7=1 selects RV32M. + let instr_word = encode_r_type( + /*funct7=*/ 0b0000001, funct3, /*rd=*/ 1, /*rs1=*/ 2, /*rs2=*/ 3, + ); + let row = rv32_decode_lookup_backed_row_from_instr_word(&layout, instr_word, /*active=*/ true); + + let base_id = shout.opcode_to_id(base_alu_opcode_for_funct3(funct3)).0 as u64; + let rv32m_id = shout.opcode_to_id(rv32m_opcode_for_funct3(funct3)).0 as u64; + let expected_delta = rv32m_id - base_id; + + assert_eq!( + row[layout.shout_table_id], + F::from_u64(rv32m_id), + "decode lookup shout table id mismatch for funct3={funct3}" + ); + assert_eq!( + row[layout.alu_reg_table_delta], + F::from_u64(expected_delta), + "decode lookup alu_reg_table_delta mismatch for funct3={funct3}" + ); + } +} diff --git a/crates/neo-memory/tests/riscv_decode_rv32.rs b/crates/neo-memory/tests/riscv_decode_rv32.rs index d36ce3d1..7b609259 100644 --- a/crates/neo-memory/tests/riscv_decode_rv32.rs +++ b/crates/neo-memory/tests/riscv_decode_rv32.rs @@ -94,7 +94,7 @@ fn rv32_system_decode_matches_jolt_behavior() { } #[test] -fn rv32_fence_decode_accepts_fence_rejects_fence_i() { +fn rv32_fence_decode_accepts_fence_and_fence_i() { let fence = RiscvInstruction::Fence { pred: 0xf, succ: 0x0 }; let encoded = encode_instruction(&fence); let decoded = decode_instruction(encoded).expect("decode fence"); @@ -106,6 +106,28 @@ fn rv32_fence_decode_accepts_fence_rejects_fence_i() { _ => panic!("expected FENCE decode"), } + let fence_i = RiscvInstruction::FenceI; + let encoded_fence_i = encode_instruction(&fence_i); + let decoded_fence_i = decode_instruction(encoded_fence_i).expect("decode fence.i"); + match decoded_fence_i { + RiscvInstruction::FenceI => {} + _ => panic!("expected FENCE.I decode"), + } +} + +#[test] +fn rv32_fence_i_decode_rejects_invalid_reserved_fields() { + // FENCE.I requires imm[11:0]==0 and rd/rs1==x0. + let invalid_nonzero_imm = 0x0010_100fu32; + assert!(decode_instruction(invalid_nonzero_imm).is_err()); + + // FENCE.I with rd != 0 + let invalid_nonzero_rd = encode_instruction(&RiscvInstruction::FenceI) | (1u32 << 7); + assert!(decode_instruction(invalid_nonzero_rd).is_err()); + let fence_i = 0x0000_100fu32; - assert!(decode_instruction(fence_i).is_err()); + assert!(matches!( + decode_instruction(fence_i).expect("valid canonical fence.i"), + RiscvInstruction::FenceI + )); } diff --git a/crates/neo-memory/tests/riscv_packed_rollout_layout.rs b/crates/neo-memory/tests/riscv_packed_rollout_layout.rs new file mode 100644 index 00000000..9f7aedd9 --- /dev/null +++ b/crates/neo-memory/tests/riscv_packed_rollout_layout.rs @@ -0,0 +1,37 @@ +use neo_memory::riscv::lookups::{compute_op, RiscvOpcode}; +use neo_memory::riscv::packed::{build_rv32_packed_cols, rv32_packed_d}; +use p3_goldilocks::Goldilocks; + +#[test] +fn packed_rollout_cols_len_matches_declared_d() { + let cases: &[(RiscvOpcode, u32, u32)] = &[ + (RiscvOpcode::Mul, 13, 5), + (RiscvOpcode::Mulh, 0xFFFF_FFFF, 0xFFFF_FFFD), + (RiscvOpcode::Mulhu, 0xFFFF_FFFF, 0xFFFF_FFFD), + (RiscvOpcode::Mulhsu, 0xFFFF_FFFF, 5), + (RiscvOpcode::Div, 0x8000_0000, 0xFFFF_FFFF), + (RiscvOpcode::Div, 13, 0), + (RiscvOpcode::Divu, 13, 0), + (RiscvOpcode::Divu, 13, 5), + (RiscvOpcode::Rem, 0x8000_0000, 0xFFFF_FFFF), + (RiscvOpcode::Rem, 13, 0), + (RiscvOpcode::Remu, 13, 0), + (RiscvOpcode::Remu, 13, 5), + ]; + + for &(op, lhs, rhs) in cases { + let val = compute_op(op, lhs as u64, rhs as u64, 32) as u32; + let cols = build_rv32_packed_cols::(op, lhs, rhs, val) + .unwrap_or_else(|e| panic!("packed col synthesis failed for {op:?}: {e}")); + let d = rv32_packed_d(op).unwrap_or_else(|e| panic!("packed d failed for {op:?}: {e}")); + assert_eq!(cols.len(), d, "packed columns length mismatch for opcode={op:?}"); + } +} + +#[test] +fn packed_rollout_rejects_incorrect_value() { + let err = build_rv32_packed_cols::(RiscvOpcode::Mul, 13, 5, 64) + .expect_err("packed col synthesis must reject mismatched val"); + let msg = err.to_string(); + assert!(msg.contains("value mismatch"), "unexpected mismatch error: {msg}"); +} diff --git a/crates/nightstream-sdk/guest/riscv32im-unknown-none-elf.ld b/crates/nightstream-sdk/guest/riscv32im-unknown-none-elf.ld index d3f511a2..0d524b20 100644 --- a/crates/nightstream-sdk/guest/riscv32im-unknown-none-elf.ld +++ b/crates/nightstream-sdk/guest/riscv32im-unknown-none-elf.ld @@ -22,6 +22,25 @@ SECTIONS *(.text*) } + .rodata : ALIGN(4) + { + *(.rodata*) + *(.srodata*) + } + + .data : ALIGN(4) + { + *(.data*) + *(.sdata*) + } + + .bss (NOLOAD) : ALIGN(4) + { + *(.bss*) + *(.sbss*) + *(COMMON) + } + /DISCARD/ : { *(.eh_frame*) diff --git a/scripts/count_loc.sh b/scripts/count_loc.sh new file mode 100755 index 00000000..ade4b34e --- /dev/null +++ b/scripts/count_loc.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +CRATES=( + neo-ajtai + neo-ccs + neo-fold + neo-math + neo-memory + neo-params + neo-reductions + neo-transcript + neo-vm-trace +) + +count_loc() { + local dir="$1" + find "$dir" -name '*.rs' \ + -not -path '*/tests/*' \ + -not -path '*/test/*' \ + -not -name '*_test.rs' \ + -not -name 'test_*.rs' \ + -print0 \ + | xargs -0 awk ' + BEGIN { n = 0 } + /^\s*$/ { next } + /^\s*\/\// { next } + /^\s*\/\*/ { block=1 } + block { if (/\*\//) block=0; next } + { n++ } + END { print n } + ' 2>/dev/null +} + +total=0 + +printf "%-20s %8s\n" "Crate" "LoC" +printf "%-20s %8s\n" "--------------------" "--------" + +for crate in "${CRATES[@]}"; do + crate_dir="$REPO_ROOT/crates/$crate" + if [ ! -d "$crate_dir/src" ]; then + printf "%-20s %8s\n" "$crate" "(missing)" + continue + fi + + count=$(count_loc "$crate_dir/src") + count=${count:-0} + + printf "%-20s %8d\n" "$crate" "$count" + total=$((total + count)) +done + +printf "%-20s %8s\n" "--------------------" "--------" +printf "%-20s %8d\n" "TOTAL" "$total"