From cf78f6629eb0d3dea94d6b72ca67d192b689197e Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Thu, 30 Jul 2026 17:57:06 +0800 Subject: [PATCH 1/8] fix(plan): support modern multi-target updates --- pkg/pb/plan/plan.pb.go | 1763 +++++++++-------- pkg/sql/colexec/multi_update/delete.go | 4 +- pkg/sql/colexec/multi_update/multi_update.go | 103 +- .../multi_update/multi_update_partition.go | 2 + .../multi_update_partition_test.go | 14 +- .../colexec/multi_update/s3writer_delegate.go | 38 +- pkg/sql/colexec/multi_update/types.go | 5 + pkg/sql/colexec/multi_update/update_test.go | 71 + pkg/sql/compile/operator.go | 2 + pkg/sql/compile/remoterun.go | 4 + pkg/sql/compile/remoterun_test.go | 18 +- pkg/sql/plan/bind_update.go | 298 ++- pkg/sql/plan/build_test.go | 67 + pkg/sql/plan/deepcopy.go | 2 + pkg/sql/plan/dml_context.go | 39 +- pkg/sql/plan/update_planner_route_test.go | 6 - proto/plan.proto | 2 + .../update/update_multi_target_modern.result | 67 + .../dml/update/update_multi_target_modern.sql | 67 + 19 files changed, 1657 insertions(+), 915 deletions(-) create mode 100644 test/distributed/cases/dml/update/update_multi_target_modern.result create mode 100644 test/distributed/cases/dml/update/update_multi_target_modern.sql diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go index 3e92b93dddfbc..852b810a31c26 100644 --- a/pkg/pb/plan/plan.pb.go +++ b/pkg/pb/plan/plan.pb.go @@ -5822,6 +5822,8 @@ type UpdateCtx struct { SkipInsertOnNullPk bool `protobuf:"varint,10,opt,name=skip_insert_on_null_pk,json=skipInsertOnNullPk,proto3" json:"skip_insert_on_null_pk,omitempty"` InsertPkColIdx int32 `protobuf:"varint,11,opt,name=insert_pk_col_idx,json=insertPkColIdx,proto3" json:"insert_pk_col_idx,omitempty"` CountDeleteAffectRows bool `protobuf:"varint,12,opt,name=count_delete_affect_rows,json=countDeleteAffectRows,proto3" json:"count_delete_affect_rows,omitempty"` + DedupByTargetRowId bool `protobuf:"varint,13,opt,name=dedup_by_target_row_id,json=dedupByTargetRowId,proto3" json:"dedup_by_target_row_id,omitempty"` + TargetUpdateCtxIdx int32 `protobuf:"varint,14,opt,name=target_update_ctx_idx,json=targetUpdateCtxIdx,proto3" json:"target_update_ctx_idx,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -5916,6 +5918,20 @@ func (m *UpdateCtx) GetCountDeleteAffectRows() bool { return false } +func (m *UpdateCtx) GetDedupByTargetRowId() bool { + if m != nil { + return m.DedupByTargetRowId + } + return false +} + +func (m *UpdateCtx) GetTargetUpdateCtxIdx() int32 { + if m != nil { + return m.TargetUpdateCtxIdx + } + return 0 +} + type InsertCtx struct { Ref *ObjectRef `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"` AddAffectedRows bool `protobuf:"varint,2,opt,name=add_affected_rows,json=addAffectedRows,proto3" json:"add_affected_rows,omitempty"` @@ -13959,853 +13975,856 @@ func init() { func init() { proto.RegisterFile("plan.proto", fileDescriptor_2d655ab2f7683c23) } var fileDescriptor_2d655ab2f7683c23 = []byte{ - // 13536 bytes of a gzipped FileDescriptorProto + // 13577 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0xbd, 0x5b, 0x8c, 0x1b, 0x49, 0x96, 0x18, 0x5a, 0x2c, 0xbe, 0x0f, 0xc9, 0xaa, 0xac, 0x50, 0x49, 0xa2, 0xd4, 0x6a, 0xa9, 0x3a, 0xa5, 0x56, 0xab, 0xd5, 0xdd, 0xea, 0x6e, 0xa9, 0x9f, 0xb3, 0xb3, 0x3b, 0xc3, 0x22, 0x59, 0x2a, 0x76, 0xb3, 0xc8, 0x9a, 0x24, 0x4b, 0xea, 0x9e, 0xbd, 0x8b, 0x44, 0x16, 0x33, 0xc9, 0xca, 0xae, 0x64, 0x26, 0x3b, 0x33, 0xa9, 0xaa, 0x1a, 0x60, 0x71, 0x07, 0xf7, 0x63, 0x1f, 0xc0, 0xfd, 0xbb, - 0xb8, 0xf7, 0x7e, 0x2d, 0xbc, 0x36, 0x60, 0x18, 0x30, 0x6c, 0x18, 0x36, 0x06, 0x58, 0xef, 0x97, - 0x3d, 0xf0, 0xcf, 0x1a, 0x7e, 0xc0, 0xb0, 0x61, 0x2f, 0xd6, 0x06, 0xd6, 0xf6, 0xf8, 0xd7, 0x86, - 0x0d, 0xd8, 0x80, 0x01, 0xff, 0xd8, 0x38, 0x27, 0x22, 0x33, 0x23, 0x49, 0x4a, 0xea, 0xee, 0x99, - 0x05, 0xfc, 0x53, 0xc5, 0x38, 0x8f, 0x78, 0xc7, 0x89, 0x73, 0x4e, 0x9c, 0x88, 0x04, 0x98, 0x39, - 0x86, 0xfb, 0x60, 0xe6, 0x7b, 0xa1, 0xc7, 0x72, 0xf8, 0xfb, 0xfa, 0x3b, 0x13, 0x3b, 0x3c, 0x99, - 0x1f, 0x3f, 0x18, 0x79, 0xd3, 0x77, 0x27, 0xde, 0xc4, 0x7b, 0x97, 0x90, 0xc7, 0xf3, 0x31, 0xa5, - 0x28, 0x41, 0xbf, 0x38, 0xd3, 0x75, 0x70, 0xbc, 0xd1, 0xa9, 0xf8, 0xbd, 0x19, 0xda, 0x53, 0x2b, - 0x08, 0x8d, 0xe9, 0x8c, 0x03, 0xd4, 0x3f, 0xca, 0x40, 0x6e, 0x78, 0x31, 0xb3, 0xd8, 0x06, 0xac, - 0xdb, 0x66, 0x3d, 0xb3, 0x93, 0xb9, 0x97, 0xd7, 0xd6, 0x6d, 0x93, 0xed, 0x40, 0xc5, 0xf5, 0xc2, - 0xde, 0xdc, 0x71, 0x8c, 0x63, 0xc7, 0xaa, 0xaf, 0xef, 0x64, 0xee, 0x95, 0x34, 0x19, 0xc4, 0x5e, - 0x81, 0xb2, 0x31, 0x0f, 0x3d, 0xdd, 0x76, 0x47, 0x7e, 0x3d, 0x4b, 0xf8, 0x12, 0x02, 0x3a, 0xee, - 0xc8, 0x67, 0xdb, 0x90, 0x3f, 0xb3, 0xcd, 0xf0, 0xa4, 0x9e, 0xa3, 0x1c, 0x79, 0x02, 0xa1, 0xc1, - 0xc8, 0x70, 0xac, 0x7a, 0x9e, 0x43, 0x29, 0x81, 0xd0, 0x90, 0x0a, 0x29, 0xec, 0x64, 0xee, 0x95, - 0x35, 0x9e, 0x60, 0x37, 0x01, 0x2c, 0x77, 0x3e, 0x7d, 0x66, 0x38, 0x73, 0x2b, 0xa8, 0x17, 0x09, - 0x25, 0x41, 0xd4, 0x1f, 0x40, 0x79, 0x1a, 0x4c, 0xf6, 0x2d, 0xc3, 0xb4, 0x7c, 0x76, 0x15, 0x8a, - 0xd3, 0x60, 0xa2, 0x87, 0xc6, 0x44, 0x34, 0xa1, 0x30, 0x0d, 0x26, 0x43, 0x63, 0xc2, 0xae, 0x41, - 0x89, 0x10, 0x17, 0x33, 0xde, 0x86, 0xbc, 0x86, 0x84, 0xd8, 0x62, 0xf5, 0xf7, 0x0b, 0x50, 0xec, - 0xda, 0xa1, 0xe5, 0x1b, 0x0e, 0xbb, 0x02, 0x05, 0x3b, 0x70, 0xe7, 0x8e, 0x43, 0xec, 0x25, 0x4d, - 0xa4, 0xd8, 0x15, 0xc8, 0xdb, 0x9f, 0x3c, 0x33, 0x1c, 0xce, 0xbb, 0xbf, 0xa6, 0xf1, 0x24, 0xab, - 0x43, 0xc1, 0x7e, 0xff, 0x23, 0x44, 0x64, 0x05, 0x42, 0xa4, 0x09, 0xf3, 0xe8, 0x21, 0x62, 0x72, - 0x31, 0x86, 0xd2, 0x84, 0xf9, 0xe8, 0x03, 0xc4, 0x60, 0xeb, 0xb3, 0x84, 0xa1, 0x34, 0x96, 0x32, - 0xa7, 0x52, 0xb0, 0x03, 0x6a, 0x58, 0xca, 0x3c, 0x2a, 0x65, 0xce, 0x4b, 0x29, 0x0a, 0x84, 0x48, - 0x13, 0x86, 0x97, 0x52, 0x8a, 0x31, 0x71, 0x29, 0x73, 0x5e, 0x4a, 0x79, 0x27, 0x73, 0x2f, 0x47, - 0x18, 0x5e, 0xca, 0x36, 0xe4, 0x4c, 0x84, 0xc3, 0x4e, 0xe6, 0x5e, 0x66, 0x7f, 0x4d, 0xa3, 0x14, - 0x42, 0x03, 0x84, 0x56, 0xb0, 0x83, 0x11, 0x1a, 0x08, 0xe8, 0x31, 0x42, 0xab, 0xd8, 0x1b, 0x08, - 0x3d, 0x16, 0xd0, 0x31, 0x42, 0x6b, 0x3b, 0x99, 0x7b, 0xeb, 0x08, 0xc5, 0x14, 0xbb, 0x0e, 0x45, - 0xd3, 0x08, 0x2d, 0x44, 0x6c, 0x88, 0x26, 0x47, 0x00, 0xc4, 0xe1, 0x8c, 0x43, 0xdc, 0xa6, 0x68, - 0x74, 0x04, 0x60, 0x2a, 0x54, 0x90, 0x2c, 0xc2, 0x2b, 0x02, 0x2f, 0x03, 0xd9, 0x87, 0x50, 0x35, - 0xad, 0x91, 0x3d, 0x35, 0x1c, 0xde, 0xa6, 0xad, 0x9d, 0xcc, 0xbd, 0xca, 0xc3, 0xcd, 0x07, 0xb4, - 0x26, 0x62, 0xcc, 0xfe, 0x9a, 0x96, 0x22, 0x63, 0x9f, 0x40, 0x4d, 0xa4, 0xdf, 0x7f, 0x48, 0x1d, - 0xcb, 0x88, 0x4f, 0x49, 0xf1, 0xbd, 0xff, 0xf0, 0x93, 0xfd, 0x35, 0x2d, 0x4d, 0xc8, 0xee, 0x40, - 0x35, 0x5e, 0x22, 0xc8, 0x78, 0x49, 0xd4, 0x2a, 0x05, 0xc5, 0x66, 0x7d, 0x15, 0x78, 0x2e, 0x12, - 0x6c, 0x8b, 0x7e, 0x8b, 0x00, 0x6c, 0x07, 0xc0, 0xb4, 0xc6, 0xc6, 0xdc, 0x09, 0x11, 0x7d, 0x59, - 0x74, 0xa0, 0x04, 0x63, 0x37, 0xa1, 0x3c, 0x9f, 0x61, 0x2b, 0x9f, 0x18, 0x4e, 0xfd, 0x8a, 0x20, - 0x48, 0x40, 0x98, 0x3b, 0xce, 0x73, 0xc4, 0x5e, 0x15, 0xa3, 0x1b, 0x01, 0x70, 0x78, 0x9f, 0x59, - 0x23, 0x44, 0xd5, 0x45, 0xc1, 0x22, 0x8d, 0xab, 0xc8, 0x0e, 0x76, 0x6d, 0xb7, 0x7e, 0x8d, 0x66, - 0x30, 0x4f, 0xb0, 0x1b, 0x90, 0x0d, 0xfc, 0x51, 0xfd, 0x3a, 0xb5, 0x1f, 0x78, 0xfb, 0xdb, 0xe7, - 0x33, 0x5f, 0x43, 0xf0, 0x6e, 0x11, 0xf2, 0xb4, 0x9a, 0xd4, 0x1b, 0x50, 0x3a, 0x34, 0x7c, 0x63, - 0xaa, 0x59, 0x63, 0xa6, 0x40, 0x76, 0xe6, 0x05, 0x62, 0x1d, 0xe1, 0x4f, 0xb5, 0x0b, 0x85, 0x27, - 0x86, 0x8f, 0x38, 0x06, 0x39, 0xd7, 0x98, 0x5a, 0x84, 0x2c, 0x6b, 0xf4, 0x1b, 0xd7, 0x4e, 0x70, - 0x11, 0x84, 0xd6, 0x54, 0x08, 0x09, 0x91, 0x42, 0xf8, 0xc4, 0xf1, 0x8e, 0xc5, 0x1a, 0x29, 0x69, - 0x22, 0xa5, 0xfe, 0x5f, 0x19, 0x28, 0x34, 0x3d, 0x07, 0xb3, 0xbb, 0x0a, 0x45, 0xdf, 0x72, 0xf4, - 0xa4, 0xb8, 0x82, 0x6f, 0x39, 0x87, 0x5e, 0x80, 0x88, 0x91, 0xc7, 0x11, 0x7c, 0xd5, 0x16, 0x46, - 0x1e, 0x21, 0xa2, 0x0a, 0x64, 0xa5, 0x0a, 0x5c, 0x83, 0x52, 0x78, 0xec, 0xe8, 0x04, 0xcf, 0x11, - 0xbc, 0x18, 0x1e, 0x3b, 0x3d, 0x44, 0x5d, 0x85, 0xa2, 0x79, 0xcc, 0x31, 0x79, 0xc2, 0x14, 0xcc, - 0x63, 0x44, 0xa8, 0x9f, 0x42, 0x59, 0x33, 0xce, 0x44, 0x35, 0x2e, 0x43, 0x01, 0x33, 0x10, 0xf2, - 0x2f, 0xa7, 0xe5, 0xc3, 0x63, 0xa7, 0x63, 0x22, 0x18, 0x2b, 0x61, 0x9b, 0x54, 0x87, 0x9c, 0x96, - 0x1f, 0x79, 0x4e, 0xc7, 0x54, 0x87, 0x00, 0x4d, 0xcf, 0xf7, 0xbf, 0x73, 0x13, 0xb6, 0x21, 0x6f, - 0x5a, 0xb3, 0xf0, 0x84, 0x8b, 0x0e, 0x8d, 0x27, 0xd4, 0xfb, 0x50, 0xc2, 0x71, 0xe9, 0xda, 0x41, - 0xc8, 0x6e, 0x42, 0xce, 0xb1, 0x83, 0xb0, 0x9e, 0xd9, 0xc9, 0x2e, 0x8c, 0x1a, 0xc1, 0xd5, 0x1d, - 0x28, 0x1d, 0x18, 0xe7, 0x4f, 0x70, 0xe4, 0x30, 0x37, 0x1a, 0x42, 0x31, 0x24, 0x62, 0x3c, 0xab, - 0x00, 0x43, 0xc3, 0x9f, 0x58, 0x21, 0x49, 0xba, 0xff, 0x9a, 0x81, 0xca, 0x60, 0x7e, 0xfc, 0xf5, - 0xdc, 0xf2, 0x2f, 0xb0, 0xce, 0xf7, 0x20, 0x1b, 0x5e, 0xcc, 0x88, 0x63, 0xe3, 0xe1, 0x15, 0x9e, - 0xbd, 0x84, 0x7f, 0x80, 0x4c, 0x1a, 0x92, 0x60, 0x23, 0x5c, 0xcf, 0xb4, 0xa2, 0x3e, 0xc8, 0x6b, - 0x05, 0x4c, 0x76, 0x4c, 0xdc, 0x2e, 0xbc, 0x99, 0x18, 0x85, 0x75, 0x6f, 0xc6, 0x76, 0x20, 0x3f, - 0x3a, 0xb1, 0x1d, 0x93, 0x06, 0x20, 0x5d, 0x67, 0x8e, 0xc0, 0x51, 0xf2, 0xbd, 0x33, 0x3d, 0xb0, - 0x7f, 0x12, 0x89, 0xff, 0xa2, 0xef, 0x9d, 0x0d, 0xec, 0x9f, 0x58, 0xea, 0x50, 0xec, 0x41, 0x00, - 0x85, 0x41, 0xb3, 0xd1, 0x6d, 0x68, 0xca, 0x1a, 0xfe, 0x6e, 0x7f, 0xd1, 0x19, 0x0c, 0x07, 0x4a, - 0x86, 0x6d, 0x00, 0xf4, 0xfa, 0x43, 0x5d, 0xa4, 0xd7, 0x59, 0x01, 0xd6, 0x3b, 0x3d, 0x25, 0x8b, - 0x34, 0x08, 0xef, 0xf4, 0x94, 0x1c, 0x2b, 0x42, 0xb6, 0xd1, 0xfb, 0x52, 0xc9, 0xd3, 0x8f, 0x6e, - 0x57, 0x29, 0xa8, 0xff, 0x63, 0x1d, 0xca, 0xfd, 0xe3, 0xaf, 0xac, 0x51, 0x88, 0x6d, 0xc6, 0x59, - 0x6a, 0xf9, 0xcf, 0x2c, 0x9f, 0x9a, 0x9d, 0xd5, 0x44, 0x0a, 0x1b, 0x62, 0x1e, 0x53, 0xe3, 0xb2, - 0xda, 0xba, 0x79, 0x4c, 0x74, 0xa3, 0x13, 0x6b, 0x6a, 0x50, 0xe3, 0x90, 0x8e, 0x52, 0xb8, 0x2a, - 0xbc, 0xe3, 0xaf, 0xa8, 0x79, 0x59, 0x0d, 0x7f, 0xb2, 0x5b, 0x50, 0xe1, 0x79, 0xc8, 0xf3, 0x0b, - 0x38, 0x68, 0x71, 0xf2, 0x15, 0xe4, 0xc9, 0x47, 0x9c, 0x94, 0x2b, 0x47, 0x8a, 0xbd, 0x8d, 0x83, - 0x7a, 0x62, 0x46, 0x7b, 0xc7, 0x5f, 0x71, 0x6c, 0x89, 0xcf, 0x68, 0xef, 0xf8, 0x2b, 0x42, 0xbd, - 0x05, 0x5b, 0xc1, 0xfc, 0x38, 0x18, 0xf9, 0xf6, 0x2c, 0xb4, 0x3d, 0x97, 0xd3, 0x94, 0x89, 0x46, - 0x91, 0x11, 0x44, 0x7c, 0x0f, 0x4a, 0xb3, 0xf9, 0xb1, 0x6e, 0xbb, 0x63, 0x8f, 0xc4, 0x7e, 0xe5, - 0x61, 0x8d, 0x0f, 0xcc, 0xe1, 0xfc, 0xb8, 0xe3, 0x8e, 0x3d, 0xad, 0x38, 0xe3, 0x3f, 0x98, 0x0a, - 0x35, 0xd7, 0x0b, 0x75, 0x54, 0x15, 0xf4, 0xa9, 0x15, 0x1a, 0xb4, 0x1f, 0xf0, 0x0d, 0xbf, 0xeb, - 0x8d, 0x4e, 0x0f, 0xac, 0xd0, 0x60, 0xf7, 0xa1, 0x14, 0xb8, 0xc6, 0x2c, 0x38, 0xf1, 0x42, 0xda, - 0x18, 0x2a, 0x0f, 0x37, 0xc4, 0xdc, 0x11, 0x50, 0x2d, 0xc6, 0xab, 0x77, 0xa1, 0x28, 0xca, 0x40, - 0x3d, 0x21, 0xb4, 0x5c, 0xc3, 0x0d, 0xf5, 0x58, 0xc1, 0x28, 0x71, 0x40, 0xc7, 0x54, 0x7f, 0x96, - 0x01, 0x65, 0x20, 0x55, 0x9b, 0x0a, 0x5a, 0x25, 0x65, 0x5e, 0x05, 0x30, 0x46, 0x23, 0x6f, 0xce, - 0xb3, 0xe1, 0x93, 0xb1, 0x2c, 0x20, 0x1d, 0x53, 0xee, 0xeb, 0x6c, 0xaa, 0xaf, 0x5f, 0x83, 0x6a, - 0xc4, 0x27, 0x09, 0x88, 0x8a, 0x80, 0x45, 0xbd, 0x1d, 0xcc, 0x53, 0x52, 0xa2, 0x18, 0xcc, 0x39, - 0xf7, 0x15, 0x28, 0x90, 0x36, 0x12, 0x44, 0x23, 0xc8, 0x53, 0xea, 0x9f, 0x66, 0xa0, 0xd6, 0x71, - 0x4d, 0xeb, 0x7c, 0x30, 0x32, 0xdc, 0xa8, 0x03, 0xed, 0x40, 0xb7, 0x11, 0xa6, 0x07, 0x23, 0xc3, - 0x15, 0x8a, 0x44, 0xc5, 0x0e, 0x62, 0x3a, 0x6c, 0x03, 0x27, 0xa0, 0xa2, 0xd6, 0x29, 0xc7, 0x32, - 0x41, 0xa8, 0xb0, 0xbb, 0xb0, 0x79, 0x6c, 0x39, 0x9e, 0x3b, 0xd1, 0x43, 0x4f, 0xe7, 0x1a, 0x11, - 0x6f, 0x4b, 0x8d, 0x83, 0x87, 0xde, 0x90, 0x34, 0xa3, 0x6d, 0xc8, 0xcf, 0x0c, 0x3f, 0x0c, 0xea, - 0xb9, 0x9d, 0x2c, 0x2e, 0x79, 0x4a, 0x60, 0x37, 0xdb, 0x81, 0x3e, 0x77, 0xed, 0xaf, 0xe7, 0xbc, - 0x19, 0x25, 0xad, 0x64, 0x07, 0x47, 0x94, 0x66, 0xf7, 0x40, 0xe1, 0x25, 0x53, 0xb6, 0xf2, 0x9c, - 0xdc, 0x20, 0x38, 0x65, 0x4c, 0x82, 0xf1, 0x3f, 0xae, 0x43, 0x69, 0x6f, 0xee, 0x8e, 0x70, 0x30, - 0xd8, 0x6d, 0xc8, 0x8d, 0xe7, 0xee, 0x88, 0xda, 0x12, 0x6f, 0xbb, 0xf1, 0x9a, 0xd2, 0x08, 0x89, - 0xd2, 0xca, 0xf0, 0x27, 0x28, 0xe5, 0x96, 0xa4, 0x15, 0xc2, 0x69, 0xe4, 0x26, 0x13, 0x7d, 0xe4, - 0xb9, 0x63, 0x7b, 0x42, 0x2d, 0xaa, 0x6a, 0x65, 0x63, 0x32, 0x69, 0x12, 0x80, 0x35, 0x60, 0x33, - 0x41, 0x73, 0x45, 0x2d, 0x47, 0x82, 0xe9, 0x1a, 0xcf, 0xa9, 0x31, 0x99, 0xf8, 0xd6, 0xc4, 0x08, - 0x2d, 0x4e, 0x4f, 0xb2, 0xa9, 0x16, 0xb3, 0x93, 0x7c, 0xfb, 0xbb, 0x19, 0x5e, 0xe7, 0x3d, 0xc7, - 0x98, 0xb0, 0x12, 0xe4, 0x7a, 0xfd, 0x5e, 0x5b, 0x59, 0x63, 0x55, 0x28, 0x75, 0x7a, 0xc3, 0xb6, - 0xd6, 0x6b, 0x74, 0x95, 0x0c, 0x09, 0x97, 0x61, 0x63, 0xb7, 0xdb, 0x56, 0xd6, 0x11, 0xf3, 0xa4, - 0xdf, 0x6d, 0x0c, 0x3b, 0xdd, 0xb6, 0x92, 0xe3, 0x18, 0xad, 0xd3, 0x1c, 0x2a, 0x25, 0xa6, 0x40, - 0xf5, 0x50, 0xeb, 0xb7, 0x8e, 0x9a, 0x6d, 0xbd, 0x77, 0xd4, 0xed, 0x2a, 0x0a, 0xbb, 0x04, 0x9b, - 0x31, 0xa4, 0xcf, 0x81, 0x3b, 0xc8, 0xf2, 0xa4, 0xa1, 0x35, 0xb4, 0xc7, 0xca, 0x0f, 0x59, 0x09, - 0xb2, 0x8d, 0xc7, 0x8f, 0x95, 0x9f, 0xa2, 0x9c, 0x2a, 0x3f, 0xed, 0xf4, 0xf4, 0x27, 0x8d, 0xee, - 0x51, 0x5b, 0xf9, 0xe9, 0x7a, 0x94, 0xee, 0x6b, 0xad, 0xb6, 0xa6, 0xfc, 0x34, 0xc7, 0xb6, 0xa0, - 0xfa, 0xe3, 0x7e, 0xaf, 0x7d, 0xd0, 0x38, 0x3c, 0xa4, 0x8a, 0xfc, 0xb4, 0xa4, 0xfe, 0xa7, 0x1c, - 0xe4, 0xb0, 0xaf, 0x98, 0x9a, 0xc8, 0xe4, 0xb8, 0x13, 0xb1, 0x71, 0xbb, 0xb9, 0x3f, 0xf9, 0xf3, - 0x5b, 0x6b, 0x5c, 0x1a, 0xbf, 0x06, 0x59, 0xc7, 0x0e, 0x69, 0xe2, 0xc4, 0x2b, 0x59, 0x68, 0xb0, - 0xfb, 0x6b, 0x1a, 0xe2, 0xd8, 0x4d, 0xc8, 0x70, 0xb1, 0x1c, 0x2f, 0xce, 0x68, 0x5f, 0xdf, 0x5f, - 0xd3, 0x32, 0x33, 0x76, 0x03, 0x32, 0xcf, 0x84, 0x8c, 0xae, 0x72, 0x3c, 0xdf, 0xd9, 0x11, 0xfb, - 0x8c, 0xed, 0x40, 0x76, 0xe4, 0x71, 0xfd, 0x34, 0xc6, 0xf3, 0x7d, 0x0e, 0xf3, 0x1f, 0x79, 0x0e, - 0xbb, 0x0d, 0x59, 0xdf, 0x38, 0xa3, 0xb9, 0x13, 0x4f, 0x88, 0x78, 0x23, 0x45, 0x22, 0xdf, 0x38, - 0xc3, 0x4a, 0x8c, 0x49, 0xaa, 0xc5, 0x95, 0x88, 0x66, 0x14, 0x16, 0x33, 0x66, 0x3b, 0x90, 0x39, - 0x23, 0xb9, 0x16, 0xab, 0x64, 0x4f, 0x6d, 0xd7, 0xf4, 0xce, 0x06, 0x33, 0x6b, 0x84, 0x14, 0x67, - 0xec, 0x75, 0xc8, 0x06, 0xf3, 0x63, 0x92, 0x6b, 0x95, 0x87, 0x5b, 0x4b, 0x3b, 0x14, 0x16, 0x14, - 0xcc, 0x8f, 0xd9, 0x5d, 0xc8, 0x8d, 0x3c, 0xdf, 0x17, 0xb2, 0x4d, 0x89, 0x2a, 0x1c, 0x6d, 0xce, - 0xa8, 0xa2, 0x22, 0x1e, 0x0b, 0x0c, 0x49, 0xa2, 0xc5, 0x44, 0xc9, 0xee, 0x88, 0x05, 0x86, 0xec, - 0x8e, 0xd8, 0x72, 0x53, 0x72, 0x2d, 0xda, 0x90, 0x31, 0x1f, 0xc4, 0xe2, 0x20, 0x4d, 0x8d, 0x73, - 0xd2, 0x7f, 0x63, 0xa2, 0x68, 0x27, 0xc6, 0x3a, 0x4d, 0x8d, 0x73, 0x76, 0x07, 0xb2, 0xcf, 0xac, - 0x11, 0xa9, 0xc2, 0x71, 0x69, 0x62, 0x90, 0x9e, 0x50, 0xf3, 0x10, 0x4d, 0x2b, 0xcb, 0x73, 0x4c, - 0xd2, 0x8a, 0xe3, 0xb1, 0xdc, 0xf3, 0x1c, 0xf3, 0x09, 0x8d, 0x25, 0x21, 0x51, 0x01, 0x31, 0xe6, - 0xe7, 0x28, 0xef, 0x14, 0xae, 0x2a, 0x18, 0xf3, 0xf3, 0x8e, 0x89, 0x5b, 0x91, 0x6b, 0x3e, 0x23, - 0x5d, 0x38, 0xa3, 0xe1, 0x4f, 0x34, 0xd6, 0x02, 0xcb, 0xb1, 0x46, 0xa1, 0xfd, 0xcc, 0x0e, 0x2f, - 0x48, 0xdb, 0xcd, 0x68, 0x32, 0x68, 0xb7, 0x00, 0x39, 0xeb, 0x7c, 0xe6, 0xab, 0xfb, 0x50, 0x14, - 0xa5, 0x2c, 0x59, 0x7c, 0xd7, 0xa0, 0x64, 0x07, 0xb8, 0x0e, 0x83, 0x50, 0x68, 0x72, 0x45, 0x3b, - 0x68, 0x62, 0x12, 0x05, 0xb2, 0x69, 0x84, 0x86, 0x58, 0xbc, 0xf4, 0x5b, 0x7d, 0x08, 0x90, 0x34, - 0x0b, 0xeb, 0xe4, 0x58, 0x6e, 0xa4, 0x34, 0x3a, 0x96, 0x1b, 0xf3, 0xac, 0x4b, 0x3c, 0xd7, 0xa0, - 0x1c, 0xeb, 0xe9, 0xac, 0x0a, 0x19, 0x43, 0x6c, 0xc6, 0x19, 0x43, 0xbd, 0x87, 0x6a, 0x73, 0xa4, - 0x89, 0xa7, 0x71, 0x98, 0x8a, 0xb6, 0xe8, 0xcc, 0xb1, 0xfa, 0x7d, 0xa8, 0x6a, 0x56, 0x30, 0x77, - 0xc2, 0xa6, 0xe7, 0xb4, 0xac, 0x31, 0x7b, 0x1b, 0x20, 0x4e, 0x07, 0x42, 0x67, 0x4a, 0xe6, 0x6e, - 0xcb, 0x1a, 0x6b, 0x12, 0x5e, 0xfd, 0xf7, 0x39, 0xd2, 0x3e, 0x5b, 0x5c, 0xed, 0x13, 0xfa, 0x5d, - 0x46, 0xd2, 0xef, 0xe2, 0xdd, 0x67, 0x3d, 0xad, 0xe3, 0x9e, 0xd8, 0xa6, 0x69, 0xb9, 0x91, 0x2e, - 0xcb, 0x53, 0x38, 0xd8, 0x86, 0x33, 0x11, 0x02, 0x8b, 0x45, 0x85, 0x4e, 0x67, 0xbe, 0x15, 0x04, - 0x5c, 0x8b, 0x32, 0x9c, 0x49, 0xb4, 0xb6, 0xf3, 0x2f, 0x5a, 0xdb, 0xd7, 0xa0, 0x84, 0x1b, 0x30, - 0xd9, 0xa0, 0x05, 0xde, 0xfb, 0xc2, 0xd8, 0x66, 0x6f, 0x40, 0x51, 0x58, 0x0f, 0x62, 0x51, 0x89, - 0xe9, 0xd2, 0xe2, 0x40, 0x2d, 0xc2, 0xb2, 0x3a, 0xaa, 0x9c, 0xd3, 0xa9, 0xe5, 0x86, 0x91, 0xd6, - 0x20, 0x92, 0xec, 0x2d, 0x28, 0x7b, 0xae, 0xce, 0x4d, 0x0c, 0xb1, 0xaa, 0xc4, 0xf4, 0xed, 0xbb, - 0x47, 0x04, 0xd5, 0x4a, 0x9e, 0xf8, 0x85, 0x55, 0x71, 0xbc, 0x33, 0x7d, 0x64, 0xf8, 0x26, 0xad, - 0xac, 0x92, 0x56, 0x74, 0xbc, 0xb3, 0xa6, 0xe1, 0x9b, 0x5c, 0x8b, 0xfa, 0xda, 0x9d, 0x4f, 0x69, - 0x35, 0xd5, 0x34, 0x91, 0x62, 0x37, 0xa0, 0x3c, 0x72, 0xe6, 0x41, 0x68, 0xf9, 0xbb, 0x17, 0xdc, - 0x68, 0xd4, 0x12, 0x00, 0xd6, 0x6b, 0xe6, 0xdb, 0x53, 0xc3, 0xbf, 0xa0, 0xa5, 0x53, 0xd2, 0xa2, - 0x24, 0x6d, 0x65, 0xa7, 0xb6, 0x79, 0xce, 0x2d, 0x47, 0x8d, 0x27, 0x90, 0xfe, 0x84, 0xec, 0xfa, - 0x80, 0xd6, 0x47, 0x49, 0x8b, 0x92, 0x34, 0x0e, 0xf4, 0x93, 0x56, 0x44, 0x59, 0x13, 0xa9, 0x94, - 0x09, 0xb0, 0xf5, 0x5c, 0x13, 0x80, 0x2d, 0x6a, 0x61, 0x9e, 0x6f, 0x4f, 0x6c, 0xa1, 0x43, 0x5d, - 0xe2, 0x5a, 0x18, 0x07, 0x11, 0xc1, 0xc7, 0x50, 0x9b, 0x58, 0xae, 0xe5, 0x1b, 0xa1, 0x65, 0xea, - 0x28, 0x17, 0xb7, 0xa9, 0xe3, 0xc4, 0x30, 0x3f, 0x8e, 0x50, 0x28, 0x6b, 0xaa, 0x13, 0x29, 0xa5, - 0x7e, 0x0d, 0x45, 0x31, 0x36, 0xb8, 0x39, 0xe2, 0xba, 0x4b, 0xcb, 0x75, 0xbe, 0x39, 0x22, 0x9c, - 0xdd, 0x86, 0x9a, 0xa8, 0x44, 0x10, 0xfa, 0xb6, 0x3b, 0x11, 0xb3, 0xae, 0xca, 0x81, 0x03, 0x82, - 0xa1, 0x0e, 0x83, 0xf3, 0x42, 0x37, 0x8e, 0x6d, 0x07, 0xd7, 0x77, 0x56, 0xe8, 0x66, 0x73, 0xc7, - 0x69, 0x70, 0x90, 0xda, 0x87, 0x52, 0x34, 0x92, 0xbf, 0x92, 0x32, 0xd5, 0x19, 0x54, 0xe5, 0x16, - 0xfe, 0x6a, 0x1a, 0xc2, 0x75, 0x94, 0x20, 0xf4, 0x7c, 0xcb, 0x8c, 0x5c, 0x46, 0x76, 0x30, 0xa0, - 0xb4, 0xfa, 0x3b, 0x19, 0xa8, 0x90, 0xae, 0xd4, 0x27, 0x4d, 0x90, 0xbd, 0x0d, 0x6c, 0xe4, 0x5b, - 0x46, 0x68, 0xe9, 0xd6, 0x79, 0xe8, 0x1b, 0x42, 0x23, 0xe2, 0x6a, 0x95, 0xc2, 0x31, 0x6d, 0x44, - 0x70, 0xa5, 0xe8, 0x16, 0x54, 0x66, 0x86, 0x1f, 0x44, 0xda, 0x38, 0x2f, 0x1d, 0x38, 0x48, 0xe8, - 0xc2, 0x8a, 0x3b, 0xf1, 0x8d, 0xa9, 0x1e, 0x7a, 0xa7, 0x96, 0xcb, 0xed, 0x10, 0x6e, 0x81, 0x6d, - 0x10, 0x7c, 0x88, 0x60, 0x32, 0x47, 0xfe, 0x75, 0x06, 0x6a, 0x87, 0x7c, 0x82, 0x7e, 0x6e, 0x5d, - 0xb4, 0xb8, 0xd9, 0x3b, 0x8a, 0x84, 0x4b, 0x4e, 0xa3, 0xdf, 0xec, 0x26, 0x54, 0x66, 0xa7, 0xd6, - 0x85, 0x9e, 0x32, 0x11, 0xcb, 0x08, 0x6a, 0x92, 0x18, 0x79, 0x13, 0x0a, 0x1e, 0x35, 0x44, 0x6c, - 0xc7, 0x62, 0x17, 0x93, 0x5a, 0xa8, 0x09, 0x02, 0xd4, 0x1d, 0xe3, 0xac, 0x64, 0x25, 0x55, 0x64, - 0x46, 0xd5, 0xdf, 0x86, 0x3c, 0xa2, 0x82, 0x7a, 0x9e, 0x2b, 0x7d, 0x94, 0x60, 0xef, 0x41, 0x6d, - 0xe4, 0x4d, 0x67, 0x7a, 0xc4, 0x2e, 0x36, 0xe6, 0xb4, 0xf8, 0xab, 0x20, 0xc9, 0x21, 0xcf, 0x4b, - 0xfd, 0x79, 0x16, 0x4a, 0x54, 0x07, 0x21, 0x01, 0x6d, 0xf3, 0x3c, 0x92, 0x80, 0x65, 0x2d, 0x6f, - 0x9b, 0xb8, 0xc1, 0xbc, 0x44, 0x4f, 0x8d, 0xf5, 0xcf, 0xac, 0xac, 0x7f, 0x5e, 0x81, 0x82, 0x50, - 0x3e, 0x73, 0x5c, 0x44, 0xce, 0x9f, 0xaf, 0x7a, 0xe6, 0x57, 0xa9, 0x9e, 0x38, 0x84, 0x9c, 0xc6, - 0x3a, 0xc7, 0xad, 0x98, 0x4b, 0x41, 0x20, 0x50, 0x1b, 0x21, 0xb2, 0x7c, 0x2b, 0xa6, 0xe5, 0x5b, - 0x1d, 0x8a, 0xcf, 0xec, 0xc0, 0xc6, 0x09, 0x52, 0xe2, 0x12, 0x43, 0x24, 0xa5, 0x61, 0x28, 0xbf, - 0x6c, 0x18, 0xe2, 0x66, 0x1b, 0xce, 0x84, 0xdb, 0x4b, 0x51, 0xb3, 0x1b, 0xce, 0xc4, 0x63, 0xef, - 0xc3, 0xe5, 0x04, 0x2d, 0x5a, 0x43, 0xea, 0x2a, 0xb9, 0xce, 0x34, 0x16, 0x53, 0x52, 0x8b, 0xc8, - 0xa0, 0xbd, 0x0f, 0x5b, 0x12, 0xcb, 0x0c, 0x35, 0xb1, 0x80, 0xc4, 0x63, 0x59, 0xdb, 0x8c, 0xc9, - 0x49, 0x41, 0x0b, 0xd8, 0x9b, 0xd8, 0x4f, 0x23, 0x67, 0x6e, 0x72, 0x61, 0x33, 0x9f, 0xba, 0x41, - 0xbd, 0x46, 0x1d, 0xbc, 0x19, 0xc1, 0x9b, 0x1c, 0xac, 0xfe, 0xc3, 0x75, 0xa8, 0xed, 0x79, 0xbe, - 0x65, 0x4f, 0xdc, 0x64, 0x82, 0x2e, 0x59, 0x4c, 0xd1, 0xa4, 0x5d, 0x97, 0x26, 0xed, 0x2d, 0xa8, - 0x8c, 0x39, 0xa3, 0x1e, 0x1e, 0x73, 0xc7, 0x4c, 0x4e, 0x03, 0x01, 0x1a, 0x1e, 0x3b, 0x28, 0x6a, - 0x22, 0x02, 0x62, 0xce, 0x11, 0x73, 0xc4, 0x84, 0x3b, 0x28, 0xfb, 0x1e, 0xed, 0x25, 0xa6, 0xe5, - 0x58, 0x21, 0x1f, 0xc9, 0x8d, 0x87, 0xaf, 0x46, 0xfa, 0x8b, 0x54, 0xa7, 0x07, 0x9a, 0x35, 0x6e, - 0x90, 0xd2, 0x87, 0x5b, 0x4b, 0x8b, 0xc8, 0x05, 0xaf, 0xd8, 0x87, 0x0a, 0xdf, 0x90, 0x97, 0x8b, - 0x35, 0x75, 0x08, 0xe5, 0x18, 0x8c, 0x1a, 0xbc, 0xd6, 0x16, 0x5a, 0xfb, 0x1a, 0xab, 0x40, 0xb1, - 0xd9, 0x18, 0x34, 0x1b, 0xad, 0xb6, 0x92, 0x41, 0xd4, 0xa0, 0x3d, 0xe4, 0x9a, 0xfa, 0x3a, 0xdb, - 0x84, 0x0a, 0xa6, 0x5a, 0xed, 0xbd, 0xc6, 0x51, 0x77, 0xa8, 0x64, 0x59, 0x0d, 0xca, 0xbd, 0xbe, - 0xde, 0x68, 0x0e, 0x3b, 0xfd, 0x9e, 0x92, 0x53, 0x7f, 0x08, 0xa5, 0xe6, 0x89, 0x35, 0x3a, 0x7d, - 0x5e, 0x2f, 0x92, 0x63, 0xc3, 0x1a, 0x9d, 0x0a, 0xad, 0x7b, 0xc1, 0xb1, 0x61, 0x8d, 0x4e, 0xd5, - 0x36, 0x94, 0x0f, 0x0d, 0x3f, 0xb4, 0xa9, 0x5e, 0x9f, 0x40, 0x2d, 0x4e, 0xb4, 0xac, 0x71, 0xa4, - 0x8f, 0xb0, 0x58, 0x17, 0x8f, 0x51, 0x5a, 0x9a, 0x50, 0x7d, 0x1b, 0xaa, 0x32, 0x80, 0xdd, 0x80, - 0xac, 0x69, 0x8d, 0x57, 0xc8, 0x5b, 0x04, 0xab, 0x4f, 0xa0, 0xda, 0x8c, 0xf6, 0xd7, 0xe7, 0x55, - 0xfd, 0x21, 0x6c, 0x90, 0x70, 0x18, 0x1d, 0x47, 0xd2, 0x61, 0x7d, 0x85, 0x74, 0xa8, 0x22, 0x4d, - 0xf3, 0x58, 0x88, 0x87, 0x0f, 0xa1, 0x72, 0xe8, 0x7b, 0x33, 0xcb, 0x0f, 0x29, 0x5b, 0x05, 0xb2, - 0xa7, 0xd6, 0x85, 0xc8, 0x15, 0x7f, 0x26, 0xfe, 0xa6, 0x75, 0xd9, 0xdf, 0xf4, 0x10, 0x4a, 0x11, - 0xdb, 0x37, 0xe6, 0xf9, 0x01, 0x4a, 0x59, 0xe2, 0xb1, 0xad, 0x00, 0x0b, 0x7b, 0x00, 0x30, 0x8b, - 0x01, 0xa2, 0xe3, 0x22, 0x23, 0x46, 0x64, 0xae, 0x49, 0x14, 0xea, 0xab, 0x50, 0x7c, 0x62, 0x5b, - 0x67, 0xa2, 0xf9, 0xcf, 0x6c, 0xeb, 0x2c, 0x6a, 0x3e, 0xfe, 0x56, 0xff, 0x7b, 0x19, 0x4a, 0xb4, - 0x14, 0x5b, 0xcf, 0x77, 0xf1, 0x7d, 0x1b, 0x5d, 0x6f, 0x47, 0xac, 0xa7, 0xdc, 0x0a, 0x0d, 0x93, - 0xaf, 0xae, 0x57, 0x01, 0x24, 0xb1, 0xc0, 0x85, 0x5c, 0x39, 0x8c, 0xa5, 0x01, 0x2a, 0x49, 0xb4, - 0x6d, 0x05, 0x5f, 0x3b, 0xc2, 0xfa, 0x4e, 0x00, 0xec, 0x01, 0x57, 0x61, 0xc8, 0xde, 0xe6, 0x6a, - 0xde, 0xa5, 0xc8, 0x54, 0x39, 0x76, 0xac, 0xc8, 0x80, 0x22, 0xbd, 0x06, 0x13, 0x24, 0xf2, 0x2c, - 0x3f, 0x40, 0xc9, 0x46, 0x67, 0x00, 0x5a, 0x94, 0x64, 0x6f, 0x40, 0x0e, 0xf7, 0x03, 0x61, 0xf0, - 0x5c, 0x8a, 0x7a, 0x50, 0xda, 0xd0, 0x34, 0x22, 0x60, 0xf7, 0xa0, 0x48, 0x52, 0xc8, 0x42, 0xa1, - 0x24, 0xf5, 0x76, 0xb4, 0x3f, 0x68, 0x11, 0x9a, 0xbd, 0x09, 0xf9, 0xf1, 0xa9, 0x75, 0xc1, 0x25, - 0x52, 0x9c, 0x67, 0x6a, 0xcd, 0x6a, 0x9c, 0x82, 0xdd, 0x81, 0x0d, 0xdf, 0x1a, 0xeb, 0xe4, 0xf4, - 0x43, 0x21, 0x13, 0xd4, 0x37, 0x48, 0x86, 0x54, 0x7d, 0x6b, 0xdc, 0x44, 0xe0, 0xf0, 0xd8, 0x09, - 0xd8, 0x5d, 0x28, 0xd0, 0xea, 0x41, 0x0d, 0x4f, 0x2a, 0x39, 0x5a, 0x8a, 0x9a, 0xc0, 0xb2, 0xf7, - 0x01, 0x84, 0x1e, 0xa9, 0x1f, 0x5f, 0x90, 0xb3, 0x3c, 0x5e, 0x4c, 0xf2, 0xfc, 0x97, 0xb5, 0xcd, - 0x37, 0x20, 0x8f, 0x93, 0x24, 0xa8, 0x5f, 0xa5, 0x9c, 0xb7, 0xd2, 0x33, 0x88, 0x6a, 0x4a, 0x78, - 0x76, 0x0f, 0x4a, 0x38, 0x51, 0x74, 0x1c, 0x8e, 0xba, 0xac, 0x58, 0x8b, 0x59, 0x85, 0x9b, 0x88, - 0x75, 0x36, 0xf8, 0xda, 0x61, 0xf7, 0x21, 0x67, 0xe2, 0x62, 0xbe, 0x46, 0x39, 0x5e, 0x91, 0xc6, - 0x05, 0x85, 0x55, 0xcb, 0x1a, 0x93, 0xae, 0x4f, 0x34, 0x6c, 0x1f, 0x36, 0x70, 0x1a, 0x3d, 0x24, - 0xbd, 0x00, 0xbb, 0xaf, 0x7e, 0x9d, 0xb8, 0x5e, 0x5b, 0xe0, 0xea, 0x09, 0x22, 0xea, 0xec, 0xb6, - 0x1b, 0xfa, 0x17, 0x5a, 0xcd, 0x95, 0x61, 0xec, 0x3a, 0x1a, 0x64, 0x5d, 0x6f, 0x74, 0x6a, 0x99, - 0xf5, 0x57, 0x22, 0x65, 0x89, 0xa7, 0xd9, 0xa7, 0x50, 0xa3, 0x89, 0x85, 0x49, 0x2c, 0xbc, 0x7e, - 0x83, 0x84, 0xa9, 0x3c, 0x65, 0x22, 0x94, 0x96, 0xa6, 0x44, 0x11, 0x6f, 0x07, 0x7a, 0x68, 0x4d, - 0x67, 0x9e, 0x8f, 0x2a, 0xf9, 0xab, 0x91, 0xa3, 0x6a, 0x18, 0x81, 0x70, 0xcf, 0x8e, 0x8f, 0xf6, - 0x74, 0x6f, 0x3c, 0x0e, 0xac, 0xb0, 0x7e, 0x93, 0xd6, 0xcd, 0x46, 0x74, 0xc2, 0xd7, 0x27, 0x28, - 0xed, 0x99, 0x81, 0x6e, 0x5e, 0xb8, 0xc6, 0xd4, 0x1e, 0xd5, 0x6f, 0x71, 0xcd, 0xdf, 0x0e, 0x5a, - 0x1c, 0x20, 0x2b, 0xdf, 0x3b, 0x29, 0xe5, 0xfb, 0x12, 0xe4, 0xcd, 0x63, 0x5c, 0x8e, 0xaf, 0x51, - 0xb6, 0x39, 0xf3, 0xb8, 0x63, 0xb2, 0x77, 0xa0, 0x3c, 0x8b, 0x44, 0x60, 0x5d, 0x95, 0x5d, 0x0c, - 0xb1, 0x64, 0xd4, 0x12, 0x0a, 0xb4, 0x7a, 0xf7, 0x2c, 0x23, 0x9c, 0xfb, 0xd6, 0x9e, 0x63, 0x4c, - 0xea, 0xb7, 0x29, 0x27, 0x19, 0x84, 0xb5, 0x73, 0xbc, 0x89, 0x3d, 0x32, 0x68, 0xe5, 0xdf, 0xe1, - 0x2a, 0x9a, 0x80, 0x74, 0xcc, 0x44, 0x67, 0x35, 0x84, 0xde, 0xf5, 0xba, 0xac, 0xb3, 0x1a, 0x4e, - 0xe4, 0x95, 0x4b, 0xfa, 0xc2, 0x9a, 0x79, 0xa3, 0x93, 0xfa, 0x5d, 0x5a, 0x6f, 0xb5, 0xa8, 0x2b, - 0xda, 0x08, 0xbc, 0xfe, 0x98, 0x94, 0x7e, 0xea, 0xe1, 0x0f, 0x17, 0x04, 0x59, 0x6a, 0x19, 0x4a, - 0x12, 0x6f, 0x7f, 0x4d, 0x96, 0x67, 0xbb, 0x79, 0x92, 0xf8, 0xd7, 0x7f, 0x08, 0x6c, 0x79, 0x6e, - 0xbc, 0x4c, 0xaa, 0xe6, 0x85, 0x54, 0xfd, 0xde, 0xfa, 0x27, 0x19, 0xf5, 0x09, 0xd4, 0x52, 0x42, - 0x63, 0xe5, 0xee, 0xc0, 0xb5, 0x38, 0x63, 0x2a, 0x0c, 0x74, 0x9e, 0x88, 0x34, 0x74, 0xdb, 0x9d, - 0x08, 0xef, 0x23, 0xd7, 0xd0, 0x29, 0xad, 0xfe, 0xd3, 0x2c, 0x54, 0xf7, 0x8d, 0xe0, 0xe4, 0xc0, - 0x98, 0x0d, 0x42, 0x23, 0x0c, 0x70, 0x2a, 0x9d, 0x18, 0xc1, 0xc9, 0xd4, 0x98, 0x71, 0x7d, 0x3a, - 0xc3, 0x1d, 0x0f, 0x02, 0x86, 0xca, 0x34, 0x4e, 0x62, 0x4c, 0xf6, 0xdd, 0xc3, 0xcf, 0x85, 0x57, - 0x21, 0x4e, 0xa3, 0x08, 0x0b, 0x4e, 0xe6, 0xe3, 0x71, 0x5c, 0x54, 0x94, 0x64, 0x77, 0xa0, 0x26, - 0x7e, 0x92, 0x32, 0x7d, 0x2e, 0x8e, 0x91, 0xd3, 0x40, 0xf6, 0x08, 0x2a, 0x02, 0x30, 0x8c, 0x04, - 0xee, 0x46, 0xec, 0x2d, 0x4a, 0x10, 0x9a, 0x4c, 0xc5, 0x7e, 0x04, 0x97, 0xa5, 0xe4, 0x9e, 0xe7, - 0x1f, 0xcc, 0x9d, 0xd0, 0x6e, 0xf6, 0x84, 0x3a, 0xf2, 0xca, 0x12, 0x7b, 0x42, 0xa2, 0xad, 0xe6, - 0x4c, 0xd7, 0xf6, 0xc0, 0x76, 0x49, 0x7e, 0x67, 0xb5, 0x34, 0x70, 0x81, 0xca, 0x38, 0x27, 0xb1, - 0x9d, 0xa6, 0x32, 0xce, 0x71, 0x61, 0x0b, 0xc0, 0x81, 0x15, 0x9e, 0x78, 0x26, 0xa9, 0xad, 0xf1, - 0xc2, 0x1e, 0xc8, 0x28, 0x2d, 0x4d, 0x89, 0xdd, 0x89, 0x26, 0xe1, 0xc8, 0x0d, 0x49, 0x79, 0xcd, - 0x6a, 0x51, 0x12, 0xb7, 0x34, 0xdf, 0x70, 0x27, 0x56, 0x50, 0xaf, 0xec, 0x64, 0xef, 0x65, 0x34, - 0x91, 0x52, 0xff, 0xea, 0x3a, 0xe4, 0xf9, 0x48, 0xbe, 0x02, 0xe5, 0x63, 0x72, 0xfe, 0xa3, 0x6d, - 0x2f, 0x9c, 0xf4, 0x04, 0xe8, 0xcd, 0xa7, 0x5c, 0x93, 0x14, 0x5e, 0xa1, 0x8c, 0x46, 0xbf, 0x31, - 0x4b, 0x6f, 0x1e, 0x62, 0x59, 0x59, 0x82, 0x8a, 0x14, 0x56, 0xc2, 0xf7, 0xce, 0x68, 0x36, 0xe4, - 0x08, 0x11, 0x25, 0xe9, 0x1c, 0x80, 0x76, 0x47, 0x64, 0xca, 0x13, 0xae, 0x44, 0x80, 0xa6, 0x1b, - 0x2e, 0x7a, 0xb0, 0x0a, 0x4b, 0x1e, 0x2c, 0x76, 0x13, 0x50, 0x4f, 0x1d, 0x59, 0x7d, 0xd7, 0x6a, - 0xf6, 0xa8, 0x87, 0x4b, 0x9a, 0x04, 0xc1, 0x05, 0x62, 0x7a, 0x33, 0xea, 0xd4, 0xbc, 0x86, 0x3f, - 0xd9, 0x47, 0xf1, 0xec, 0xa4, 0x36, 0x0a, 0x03, 0x40, 0xec, 0x1e, 0xf2, 0x3c, 0xd6, 0x52, 0x74, - 0x98, 0x13, 0x6e, 0x09, 0xdc, 0x00, 0xc0, 0x9f, 0x6a, 0x1b, 0x40, 0xf3, 0xce, 0x02, 0x2b, 0x24, - 0x57, 0xed, 0x55, 0x6a, 0x62, 0xea, 0xc8, 0xcf, 0x3b, 0x3b, 0xf4, 0x82, 0xd8, 0x46, 0x5e, 0x5f, - 0x6d, 0x23, 0xab, 0xef, 0x42, 0x11, 0xf5, 0x05, 0x23, 0x34, 0xd8, 0x1d, 0xe1, 0x1d, 0xe3, 0x5a, - 0x8e, 0x70, 0x13, 0x26, 0x65, 0x08, 0x7f, 0x59, 0x37, 0x2a, 0x97, 0x78, 0x5e, 0x93, 0xac, 0xd0, - 0x78, 0xaf, 0x12, 0x19, 0x0a, 0x0d, 0xe4, 0x15, 0x28, 0x63, 0xd5, 0xe8, 0x6c, 0x43, 0xc8, 0x85, - 0x92, 0xef, 0x9d, 0x35, 0x31, 0xad, 0xfe, 0x9b, 0x0c, 0x54, 0xfa, 0xbe, 0x89, 0x9b, 0xe4, 0x60, - 0x66, 0x8d, 0x5e, 0x6a, 0xd2, 0xa3, 0xbe, 0xe2, 0x39, 0x8e, 0x41, 0xe2, 0x58, 0x58, 0x81, 0x31, - 0x80, 0xbd, 0x0f, 0xb9, 0x31, 0x8a, 0xdd, 0xac, 0xac, 0xc5, 0x4b, 0xd9, 0x47, 0xbf, 0x51, 0x10, - 0x6b, 0x44, 0xaa, 0xfe, 0x66, 0x5c, 0x3e, 0x49, 0x67, 0xd9, 0x3f, 0xbf, 0x46, 0xe7, 0x76, 0x83, - 0xa6, 0x92, 0x61, 0x25, 0xc8, 0xb5, 0xda, 0x83, 0x26, 0xd7, 0xdd, 0x51, 0x8b, 0x1f, 0xe8, 0x7b, - 0x1d, 0x6d, 0x30, 0x54, 0x72, 0x74, 0x10, 0x48, 0x80, 0x6e, 0x63, 0x30, 0x54, 0x4a, 0x0c, 0xa0, - 0x70, 0xd4, 0xeb, 0xfc, 0xe8, 0xa8, 0xad, 0x28, 0xea, 0xbf, 0xc8, 0x00, 0x24, 0x6e, 0x64, 0xf6, - 0x16, 0x54, 0xce, 0x28, 0xa5, 0x4b, 0x27, 0x18, 0x72, 0x1b, 0x81, 0xa3, 0x49, 0x97, 0x7a, 0x07, - 0xaa, 0xf1, 0xb6, 0x82, 0x7a, 0xc6, 0xf2, 0x51, 0x46, 0x25, 0xc6, 0xef, 0x5e, 0xb0, 0xb7, 0xa1, - 0xe4, 0x61, 0x3b, 0x90, 0x34, 0x2b, 0x2b, 0x19, 0x52, 0xf3, 0xb5, 0xa2, 0xc7, 0x13, 0xa8, 0x8f, - 0x8c, 0xfd, 0xc8, 0xaa, 0x8f, 0x49, 0xf7, 0x10, 0xd4, 0x74, 0x8c, 0x79, 0x60, 0x69, 0x1c, 0x1f, - 0x4b, 0xe9, 0x7c, 0x22, 0xa5, 0xd5, 0x1f, 0xc3, 0xc6, 0xc0, 0x98, 0xce, 0xb8, 0x2c, 0xa7, 0x86, - 0x31, 0xc8, 0xe1, 0x9c, 0x10, 0x53, 0x8f, 0x7e, 0xe3, 0xa2, 0x3b, 0xb4, 0xfc, 0x91, 0xe5, 0x46, - 0x6b, 0x34, 0x4a, 0xa2, 0xf8, 0x3d, 0x42, 0x69, 0xae, 0x79, 0x67, 0x91, 0x38, 0x8f, 0xd2, 0xea, - 0xdf, 0xc8, 0x40, 0x45, 0xaa, 0x06, 0x7b, 0x17, 0x72, 0xa4, 0xb8, 0x66, 0x64, 0x41, 0x28, 0x11, - 0xf0, 0xdf, 0x5c, 0xd5, 0x41, 0x42, 0x76, 0x17, 0xf2, 0x41, 0x68, 0xf8, 0xd1, 0x89, 0x84, 0x22, - 0x71, 0xec, 0x7a, 0x73, 0xd7, 0xd4, 0x38, 0x9a, 0xa9, 0x90, 0xb5, 0x5c, 0x53, 0xf8, 0x41, 0x96, - 0xa9, 0x10, 0xa9, 0xee, 0x40, 0x39, 0xce, 0x1e, 0xa7, 0x80, 0xd6, 0x7f, 0x3a, 0x50, 0xd6, 0x58, - 0x19, 0xf2, 0x5a, 0xa3, 0xf7, 0xb8, 0xad, 0x64, 0xd4, 0x9f, 0x65, 0x00, 0x12, 0x2e, 0xf6, 0x20, - 0x55, 0xdb, 0xeb, 0x8b, 0xb9, 0x3e, 0xa0, 0xbf, 0x52, 0x65, 0x6f, 0x40, 0x79, 0xee, 0x12, 0xd0, - 0x32, 0xc5, 0x4e, 0x94, 0x00, 0xd0, 0xda, 0x8a, 0xa2, 0x79, 0x16, 0xac, 0xad, 0x67, 0x86, 0xa3, - 0x7e, 0x0f, 0xca, 0x71, 0x76, 0x68, 0x40, 0xee, 0xf5, 0xbb, 0xdd, 0xfe, 0xd3, 0x4e, 0xef, 0xb1, - 0xb2, 0x86, 0xc9, 0x43, 0xad, 0xdd, 0x6c, 0xb7, 0x30, 0x99, 0xc1, 0x39, 0xdb, 0x3c, 0xd2, 0xb4, - 0x76, 0x6f, 0xa8, 0x6b, 0xfd, 0xa7, 0xca, 0xba, 0xfa, 0xbb, 0x19, 0xa8, 0xf5, 0x1d, 0xb4, 0xdd, - 0x9b, 0xc6, 0x0c, 0x35, 0x13, 0xf6, 0x03, 0xd8, 0x3a, 0x9e, 0xa3, 0x76, 0x3c, 0x73, 0x8c, 0x91, - 0x75, 0xe2, 0x39, 0xa6, 0x15, 0x2d, 0xc2, 0xd4, 0x99, 0x8b, 0x70, 0x0f, 0x2b, 0x44, 0x7c, 0x98, - 0xd0, 0xb2, 0x0f, 0xa1, 0x3a, 0xf3, 0xbd, 0x63, 0x4b, 0x0f, 0xbc, 0xb9, 0x3f, 0xb2, 0x96, 0xcc, - 0xba, 0x84, 0xb7, 0x42, 0x74, 0x03, 0x22, 0x53, 0xff, 0xd6, 0x3a, 0x54, 0x5b, 0x96, 0x39, 0x9f, - 0x7d, 0xe6, 0xd9, 0x6e, 0x33, 0x3c, 0x67, 0x1f, 0x40, 0xd5, 0x73, 0xc8, 0xdb, 0xa0, 0x4b, 0xf1, - 0x06, 0xab, 0xf2, 0x01, 0x8f, 0x5a, 0x40, 0xd1, 0x09, 0xef, 0xc0, 0x25, 0x6e, 0xc0, 0x0b, 0xd7, - 0xd7, 0x39, 0x67, 0xc6, 0x35, 0x93, 0xd7, 0x14, 0x8e, 0xe2, 0x1b, 0x34, 0x91, 0xff, 0x1a, 0x6c, - 0x4b, 0xe4, 0x28, 0x58, 0x38, 0x7d, 0x76, 0x69, 0x8d, 0x6d, 0xc5, 0xbc, 0x71, 0x24, 0xc4, 0x67, - 0xb0, 0x1d, 0xd5, 0x70, 0xc4, 0x7b, 0x8f, 0x33, 0xe7, 0x64, 0x33, 0x24, 0xd5, 0xbb, 0xa2, 0xc2, - 0x5b, 0x9e, 0x0c, 0xa4, 0xbc, 0xde, 0x87, 0xcb, 0x26, 0xb6, 0x5e, 0xe7, 0x9d, 0x7f, 0x6a, 0x59, - 0x33, 0xdd, 0x31, 0x82, 0x50, 0x1c, 0x96, 0x32, 0x42, 0xee, 0x22, 0xee, 0x73, 0xcb, 0x9a, 0x75, - 0x8d, 0x20, 0x54, 0xff, 0x52, 0x16, 0xca, 0xdc, 0xfb, 0x80, 0xdd, 0x75, 0x0f, 0x8a, 0xde, 0xf1, - 0x57, 0xba, 0x1f, 0x5b, 0xe5, 0x4b, 0x07, 0xa2, 0x05, 0xef, 0xf8, 0x2b, 0xcd, 0x1a, 0xb3, 0xb7, - 0xa2, 0xad, 0x0e, 0x2d, 0xf8, 0x75, 0xd9, 0xdd, 0x1e, 0xa9, 0xff, 0x62, 0xeb, 0x43, 0xd3, 0xf4, - 0x11, 0x54, 0x6c, 0x37, 0xb0, 0xfc, 0x90, 0x7b, 0x5c, 0x8a, 0xcf, 0x1f, 0x04, 0x4e, 0x46, 0x4e, - 0x98, 0x47, 0x50, 0xe1, 0x1e, 0x18, 0xce, 0x54, 0x7a, 0x3e, 0x13, 0x27, 0x23, 0xa6, 0x4f, 0x61, - 0x23, 0x11, 0x73, 0xc4, 0x57, 0x7e, 0x2e, 0x5f, 0x2d, 0xa6, 0x24, 0xd6, 0x87, 0x70, 0x25, 0x38, - 0xb5, 0x67, 0xba, 0xa8, 0xa9, 0xe7, 0xd2, 0x49, 0x85, 0x3e, 0x3b, 0x15, 0x27, 0x04, 0x0c, 0xb1, - 0x1d, 0x42, 0xf6, 0xdd, 0xde, 0xdc, 0x71, 0x0e, 0x4f, 0xd9, 0x9b, 0xb0, 0x25, 0xc8, 0x67, 0xa7, - 0xd1, 0x5c, 0x21, 0xa3, 0x34, 0xaf, 0x6d, 0x70, 0xc4, 0xe1, 0xa9, 0xd0, 0xe4, 0x3e, 0x86, 0x3a, - 0x3f, 0xa3, 0x17, 0x8d, 0x32, 0xc6, 0x63, 0x6b, 0x14, 0xea, 0xa8, 0x3a, 0x88, 0xe3, 0x84, 0xcb, - 0x84, 0xe7, 0x6e, 0xa4, 0x06, 0x61, 0x51, 0xf2, 0xa9, 0x7f, 0x3b, 0x03, 0x65, 0x5e, 0x2c, 0x8e, - 0xd0, 0x6b, 0x90, 0x7d, 0xc1, 0xe8, 0x20, 0x8e, 0xdd, 0x87, 0x2d, 0xc3, 0x34, 0x45, 0x01, 0x96, - 0xc9, 0x8b, 0xe0, 0xe2, 0x60, 0xd3, 0x30, 0xcd, 0x86, 0x80, 0x93, 0x58, 0xbd, 0x07, 0x8a, 0x1d, - 0xe8, 0x91, 0xfd, 0x99, 0x9c, 0xc8, 0x97, 0xb4, 0x0d, 0x3b, 0x10, 0xe6, 0x27, 0xf7, 0x3e, 0xa7, - 0x06, 0x3c, 0xf7, 0xe2, 0x01, 0x57, 0x19, 0x80, 0x66, 0xd1, 0xda, 0x6f, 0x86, 0xe7, 0x9f, 0xe5, - 0x4a, 0x19, 0x05, 0xd4, 0x7f, 0x54, 0x81, 0x4a, 0xc3, 0x35, 0x9c, 0x8b, 0x9f, 0x58, 0x14, 0x4e, - 0x40, 0xbe, 0xc8, 0xd9, 0x5c, 0x74, 0x01, 0x3f, 0x09, 0x2b, 0x13, 0x84, 0x6a, 0x76, 0x0b, 0x2a, - 0xde, 0x3c, 0x8c, 0xf1, 0xfc, 0x6c, 0x0c, 0x38, 0x88, 0x08, 0x62, 0xfe, 0xd8, 0xcf, 0x1d, 0xf1, - 0x93, 0x56, 0x9e, 0xf0, 0xc7, 0x9a, 0x5a, 0xcc, 0x4f, 0x04, 0xb7, 0xa1, 0x16, 0xda, 0x53, 0x8b, - 0x8e, 0x03, 0xe7, 0x53, 0xcb, 0xe4, 0x31, 0x8b, 0x3c, 0x0c, 0xae, 0x29, 0x60, 0x98, 0xcb, 0xd4, - 0x9a, 0x7a, 0xfe, 0x05, 0xcf, 0xa5, 0xc0, 0x73, 0xe1, 0x20, 0xca, 0xe5, 0x6d, 0x60, 0x67, 0x86, - 0x1d, 0xea, 0xe9, 0xac, 0xb8, 0x76, 0xac, 0x20, 0x66, 0x28, 0x67, 0x77, 0x05, 0x0a, 0xa6, 0x1d, - 0x9c, 0x76, 0xfa, 0x42, 0x33, 0x16, 0x29, 0x6c, 0x4b, 0x30, 0x32, 0x70, 0x63, 0x0e, 0x2d, 0xae, - 0xc5, 0x65, 0xb5, 0x32, 0x42, 0x76, 0x11, 0x80, 0x82, 0xdd, 0xb5, 0xc2, 0x33, 0xcf, 0x47, 0x4e, - 0xae, 0xf8, 0x26, 0x00, 0xdc, 0x00, 0x91, 0x14, 0x0b, 0xa2, 0xb9, 0x97, 0xd5, 0xe2, 0x34, 0xaa, - 0x94, 0x7c, 0x1e, 0x12, 0xb6, 0xca, 0xab, 0x9f, 0x40, 0xd8, 0x1d, 0xd8, 0xa0, 0xea, 0x93, 0x62, - 0x8c, 0x6d, 0xa0, 0xe3, 0xab, 0xac, 0x56, 0x45, 0x28, 0xd9, 0xd3, 0x48, 0xf5, 0x29, 0x5c, 0x4b, - 0xb5, 0x4f, 0x37, 0x7c, 0xdf, 0xb8, 0xd0, 0xa7, 0xc6, 0x57, 0x9e, 0x4f, 0xbe, 0x8f, 0xac, 0x76, - 0x45, 0xee, 0xb6, 0x06, 0xa2, 0x0f, 0x10, 0xfb, 0x5c, 0x56, 0xdb, 0xf5, 0x7c, 0x72, 0x8c, 0xac, - 0x64, 0x45, 0x2c, 0x59, 0xf1, 0x34, 0xc0, 0xa4, 0xa5, 0x07, 0x3c, 0x7c, 0x52, 0xab, 0x10, 0x6c, - 0x97, 0x40, 0xa8, 0xa7, 0x06, 0x8f, 0xb8, 0xbc, 0xdc, 0x12, 0xb1, 0x4c, 0x8f, 0x48, 0x12, 0x72, - 0xc4, 0x89, 0x65, 0x98, 0x74, 0x24, 0x46, 0x88, 0x7d, 0xcb, 0xa0, 0x03, 0xe7, 0xe0, 0x91, 0x3e, - 0x9b, 0x87, 0x3c, 0xee, 0x51, 0xcb, 0x07, 0x8f, 0x0e, 0xe7, 0xa1, 0x00, 0x4f, 0xac, 0x90, 0x4e, - 0xc0, 0x08, 0xfc, 0xd8, 0x0a, 0x51, 0xd9, 0x0c, 0x1e, 0x45, 0x8e, 0xe0, 0xcb, 0xa2, 0x6f, 0x1f, - 0x09, 0x4f, 0xaf, 0x0a, 0xb5, 0x18, 0xa9, 0x4f, 0xe7, 0x3c, 0xd0, 0x31, 0xab, 0x55, 0x22, 0x82, - 0x83, 0xb9, 0x83, 0x03, 0x3b, 0x32, 0x46, 0x27, 0x96, 0xee, 0x63, 0x55, 0xae, 0xf2, 0xa1, 0x23, - 0x88, 0x86, 0xb5, 0x79, 0x05, 0x78, 0x42, 0x3f, 0xb1, 0x43, 0x72, 0xd0, 0x64, 0xb5, 0x12, 0x01, - 0xf6, 0xed, 0x10, 0xd7, 0x31, 0x47, 0x8a, 0x19, 0x48, 0x59, 0x5c, 0x23, 0xa2, 0x4d, 0x42, 0x1c, - 0x10, 0x9c, 0x32, 0xba, 0x07, 0x4a, 0x8a, 0x16, 0xf3, 0xbb, 0x4e, 0xa4, 0x1b, 0x12, 0x29, 0xe6, - 0x7a, 0x17, 0x38, 0xb3, 0x8e, 0x53, 0x8f, 0xe7, 0xf9, 0x0a, 0xb7, 0xd2, 0x08, 0xdc, 0xb2, 0x83, - 0x53, 0xca, 0xf1, 0x0e, 0x6c, 0x48, 0x74, 0x98, 0xdf, 0x0d, 0x3e, 0x33, 0x62, 0xb2, 0x54, 0x1d, - 0x7d, 0x6b, 0xea, 0x85, 0xa2, 0x99, 0xaf, 0x4a, 0x75, 0xd4, 0x08, 0x9e, 0xae, 0xa3, 0xa0, 0xc5, - 0x3c, 0x6f, 0x4a, 0x75, 0xe4, 0xa4, 0x98, 0xeb, 0x6b, 0x50, 0x3d, 0xf3, 0xed, 0x30, 0xb4, 0x5c, - 0xbe, 0xf8, 0x6f, 0xf1, 0x8e, 0x15, 0x30, 0x5a, 0xfd, 0xaf, 0x41, 0x95, 0xf7, 0xbc, 0x90, 0x6f, - 0x3b, 0x9c, 0x44, 0xc0, 0x22, 0x01, 0x21, 0x7a, 0x63, 0x6a, 0xbb, 0xe4, 0x85, 0xc9, 0x6a, 0x65, - 0x0e, 0x41, 0x63, 0x55, 0x42, 0x1b, 0xe7, 0xe4, 0x8b, 0x49, 0xd0, 0xc6, 0x39, 0x2d, 0xc9, 0x99, - 0xed, 0x38, 0x7c, 0xe1, 0xdf, 0x16, 0x4b, 0x12, 0x21, 0xb4, 0xee, 0x63, 0x34, 0x95, 0x7e, 0x47, - 0x42, 0x53, 0xd9, 0x38, 0x71, 0x08, 0x8d, 0x45, 0xbf, 0x2e, 0x26, 0x0e, 0x02, 0xb0, 0xe4, 0x04, - 0x69, 0x9c, 0x93, 0xa7, 0x25, 0x46, 0x1a, 0xe7, 0x42, 0x6e, 0x61, 0xae, 0xc4, 0xfb, 0x46, 0x2c, - 0xb7, 0x10, 0x84, 0xdc, 0x32, 0x81, 0x71, 0x5e, 0xbf, 0x97, 0x26, 0x30, 0xce, 0xc9, 0x42, 0xb2, - 0x0c, 0x93, 0x57, 0xfc, 0x4d, 0x9e, 0x3d, 0x02, 0xa8, 0xde, 0x3b, 0x50, 0x0d, 0x1e, 0xe9, 0x09, - 0xfe, 0x3e, 0x67, 0x0f, 0x1e, 0x69, 0x11, 0xc5, 0x1d, 0xd8, 0x88, 0xa7, 0x06, 0xa7, 0x79, 0x8b, - 0x0f, 0xbc, 0x29, 0xa6, 0x06, 0x9d, 0x20, 0xfe, 0x34, 0x03, 0xd7, 0xfb, 0xe4, 0x44, 0x22, 0xf1, - 0x7f, 0x60, 0x05, 0x81, 0x31, 0xb1, 0xf6, 0x3c, 0x7f, 0x6f, 0xfe, 0x93, 0x9f, 0x5c, 0xb0, 0x7b, - 0xb0, 0x79, 0x68, 0xf8, 0x96, 0x1b, 0xc6, 0x67, 0x5f, 0xc2, 0x33, 0xb3, 0x08, 0x66, 0x9f, 0x80, - 0xc2, 0x41, 0x3c, 0x8e, 0xab, 0x19, 0x9d, 0xe7, 0x2c, 0xfa, 0x9f, 0x97, 0xa8, 0xd0, 0xd8, 0x2b, - 0xb7, 0xec, 0x20, 0xd4, 0xd0, 0xd2, 0x67, 0x9f, 0x82, 0xe2, 0x78, 0x67, 0x68, 0xb1, 0xa0, 0x1a, - 0xab, 0x4b, 0x8a, 0xb3, 0xd8, 0x25, 0x13, 0x6d, 0x79, 0x83, 0x08, 0x13, 0x75, 0xf7, 0x53, 0x50, - 0xe6, 0xb3, 0x59, 0x9a, 0x75, 0xfd, 0x39, 0xac, 0x44, 0x98, 0xb0, 0xbe, 0x05, 0x15, 0xa9, 0xd4, - 0x15, 0xca, 0x35, 0x24, 0x65, 0x21, 0xb1, 0x54, 0xce, 0x8a, 0x38, 0x52, 0x48, 0x72, 0x57, 0xff, - 0xef, 0x75, 0x50, 0xc8, 0x39, 0xa6, 0xd1, 0xf9, 0x3f, 0x1d, 0xa1, 0xa5, 0xcc, 0xb2, 0xcc, 0x4b, - 0xcd, 0xb2, 0x1d, 0xc8, 0x3b, 0xf6, 0x34, 0x0e, 0xa7, 0x4a, 0x1d, 0xec, 0x10, 0x02, 0xc7, 0xda, - 0xf3, 0xed, 0x09, 0x19, 0x90, 0x72, 0x68, 0x21, 0xf9, 0x07, 0xd1, 0x1e, 0xa3, 0x21, 0x7a, 0x00, - 0x60, 0xda, 0x41, 0xa8, 0x93, 0x4b, 0x45, 0x54, 0x5b, 0xf4, 0x4c, 0xdc, 0xff, 0x5a, 0xd9, 0x8c, - 0x87, 0xe2, 0x1e, 0x28, 0x92, 0x12, 0xe6, 0xc6, 0xde, 0x90, 0xbc, 0x96, 0x28, 0x67, 0x4d, 0xb7, - 0xe9, 0x86, 0x4b, 0x94, 0xa8, 0x3e, 0x15, 0x96, 0x28, 0x3b, 0xe6, 0xb9, 0xfa, 0xa7, 0x2a, 0xe4, - 0x7a, 0x9e, 0x69, 0xb1, 0xf7, 0xa0, 0x4c, 0xf1, 0xba, 0xd2, 0x00, 0x0b, 0x25, 0x19, 0xd1, 0xf4, - 0x87, 0x46, 0xaa, 0xe4, 0x8a, 0x5f, 0xcf, 0x8f, 0xf0, 0x7d, 0x8d, 0x8c, 0x3b, 0x3a, 0xe5, 0xc5, - 0x26, 0x55, 0x84, 0x03, 0x8a, 0xfc, 0x25, 0x1c, 0x83, 0x7b, 0x2b, 0xb9, 0xf9, 0x7d, 0xcb, 0x25, - 0x8d, 0x3c, 0xaf, 0xc5, 0x69, 0x32, 0xa9, 0x7d, 0x0f, 0x55, 0x2f, 0xbe, 0x03, 0xe5, 0x57, 0x98, - 0xd4, 0x1c, 0x4f, 0x5b, 0xd2, 0x7b, 0x50, 0xfe, 0xca, 0xb3, 0x5d, 0x5e, 0xf1, 0xc2, 0x52, 0xc5, - 0xd1, 0x62, 0xe1, 0x15, 0xff, 0x4a, 0xfc, 0x62, 0xb7, 0xa1, 0xe8, 0xb9, 0x3c, 0xef, 0xe2, 0x52, - 0xde, 0x05, 0xcf, 0xed, 0xf2, 0x80, 0xad, 0x9a, 0x1d, 0xe8, 0xbe, 0x3d, 0x39, 0x09, 0x75, 0xe4, - 0x14, 0xa7, 0xc3, 0x15, 0x3b, 0xd0, 0x10, 0x86, 0xd9, 0xe2, 0xc4, 0x1b, 0xdb, 0x0e, 0x6a, 0x78, - 0x94, 0x59, 0x79, 0x29, 0x33, 0xe0, 0x68, 0xca, 0xf0, 0x75, 0x28, 0x4d, 0x7c, 0x0f, 0x8d, 0x88, - 0x8b, 0x3a, 0x2c, 0x51, 0x16, 0x09, 0xb7, 0x7b, 0x81, 0xea, 0x13, 0xfd, 0xb4, 0xdd, 0x89, 0x4e, - 0x5e, 0x92, 0xca, 0x4e, 0xf6, 0x5e, 0x49, 0xab, 0x46, 0x40, 0xf2, 0x7f, 0xbc, 0x0e, 0x25, 0x63, - 0x32, 0xd1, 0x45, 0xdc, 0xd9, 0x52, 0x5e, 0xc6, 0x64, 0x42, 0x45, 0x3e, 0x80, 0xda, 0x99, 0xed, - 0xea, 0xc1, 0xcc, 0x1a, 0x71, 0xda, 0xda, 0x72, 0x57, 0x9e, 0xd9, 0x2e, 0xce, 0x6e, 0xa2, 0x97, - 0x97, 0xc1, 0xc6, 0x37, 0x5f, 0x06, 0x9b, 0xcf, 0x5b, 0x06, 0x2a, 0x14, 0xc4, 0x11, 0x80, 0xb2, - 0x44, 0x22, 0x30, 0xec, 0x7d, 0xa8, 0xf8, 0x86, 0x7b, 0xaa, 0x8b, 0xa3, 0xf6, 0x2f, 0x65, 0x4b, - 0x5f, 0x33, 0xdc, 0x53, 0x71, 0xd2, 0x0e, 0x7e, 0xfc, 0x3b, 0xad, 0x32, 0x6f, 0xbd, 0xc4, 0x46, - 0x92, 0x4c, 0x2f, 0xf6, 0x62, 0xd3, 0xeb, 0x43, 0xb2, 0x71, 0x2c, 0x37, 0xd4, 0x23, 0x86, 0x4b, - 0xab, 0x19, 0xaa, 0x9c, 0xac, 0xcf, 0xd9, 0xb0, 0x01, 0xe4, 0x69, 0xd3, 0xc9, 0x2d, 0xb7, 0x9d, - 0x6a, 0x40, 0xec, 0x82, 0xd3, 0xc0, 0x4f, 0xdc, 0x71, 0x0d, 0xd8, 0x4c, 0xc2, 0x7d, 0x79, 0x8c, - 0xf5, 0x65, 0xd9, 0xd5, 0x9f, 0x8a, 0x0f, 0x8e, 0xac, 0x2a, 0x3b, 0x15, 0x34, 0x7c, 0x1b, 0x6a, - 0x3c, 0xb6, 0x85, 0xf7, 0x5b, 0x40, 0x4a, 0x52, 0x59, 0xab, 0x12, 0x90, 0xf7, 0x53, 0x40, 0x02, - 0x46, 0x98, 0x7a, 0xe1, 0x39, 0x69, 0x49, 0x89, 0x80, 0xe1, 0xb6, 0x5d, 0x78, 0xae, 0x95, 0xcd, - 0xe8, 0x27, 0x6e, 0xfe, 0xc7, 0xb6, 0x6b, 0xe2, 0xd4, 0x0b, 0x8d, 0x49, 0x50, 0xaf, 0xd3, 0xca, - 0xac, 0x08, 0xd8, 0xd0, 0x98, 0x04, 0x68, 0xf8, 0x1b, 0xdc, 0xd8, 0xe0, 0xf5, 0xbe, 0x26, 0x7b, - 0xa6, 0x24, 0x33, 0x44, 0xab, 0x18, 0x92, 0x4d, 0xf2, 0x31, 0xb0, 0xe8, 0x84, 0x52, 0xb2, 0xe3, - 0xaf, 0x2f, 0xcd, 0xc6, 0x4d, 0x71, 0x44, 0x19, 0x5b, 0xf1, 0xb7, 0xa0, 0xc2, 0x3d, 0x15, 0x7a, - 0x10, 0x5a, 0xb3, 0xfa, 0x2b, 0x54, 0x21, 0xe0, 0xa0, 0x41, 0x68, 0xcd, 0xd8, 0xc7, 0x50, 0x4b, - 0x5b, 0x59, 0x37, 0x56, 0x1c, 0xf4, 0xd1, 0xb4, 0xd0, 0xaa, 0x23, 0xd9, 0xee, 0xba, 0xcd, 0xc3, - 0xd6, 0x49, 0x43, 0x22, 0x46, 0x7e, 0x98, 0x55, 0x75, 0xbd, 0xb0, 0x19, 0xc1, 0xb0, 0x03, 0x23, - 0x03, 0x3b, 0x3c, 0x27, 0xa5, 0x2a, 0xee, 0xc0, 0xd8, 0x74, 0x44, 0xe3, 0x28, 0xb2, 0x22, 0x5b, - 0xc0, 0x63, 0x39, 0x68, 0x93, 0xb7, 0x7c, 0x1e, 0xb7, 0x41, 0x3a, 0x54, 0x7c, 0xf6, 0xb7, 0xb8, - 0xf7, 0x68, 0x3c, 0xc6, 0x45, 0xde, 0x8d, 0x3e, 0x81, 0x8d, 0x99, 0x6f, 0xe9, 0x52, 0xc9, 0xaa, - 0xdc, 0xa8, 0x43, 0xdf, 0x4a, 0x0a, 0xaf, 0xce, 0xa4, 0x14, 0xfb, 0x01, 0x6c, 0x49, 0x9c, 0xf3, - 0x53, 0x62, 0xbe, 0x4d, 0xcc, 0xdb, 0x0b, 0xcc, 0x47, 0xa7, 0xc8, 0xbe, 0x31, 0x4b, 0xa5, 0xd9, - 0x36, 0xe4, 0x3b, 0x41, 0xdb, 0x35, 0x49, 0xb7, 0x2a, 0x69, 0x3c, 0xc1, 0x1e, 0x41, 0x95, 0x1b, - 0x32, 0x14, 0xfa, 0x1a, 0xd4, 0xef, 0xca, 0xae, 0x67, 0xb2, 0x66, 0x08, 0xa1, 0x55, 0x9c, 0xf8, - 0x77, 0xc0, 0x3e, 0x82, 0x2d, 0x7e, 0x2e, 0x20, 0x8b, 0xc8, 0x37, 0x96, 0x87, 0x9c, 0x88, 0xf6, - 0x12, 0x39, 0xa9, 0xc1, 0x35, 0x7f, 0xee, 0x92, 0x71, 0x23, 0x38, 0xb9, 0xc7, 0x8a, 0xf8, 0xef, - 0x11, 0xff, 0x55, 0xb1, 0xba, 0x38, 0x19, 0xe7, 0x25, 0xd9, 0x74, 0xc5, 0x97, 0x41, 0x87, 0xc8, - 0xf7, 0x9c, 0x3c, 0xb9, 0x27, 0x87, 0xf2, 0x7c, 0xf3, 0xdb, 0xe4, 0x49, 0x5e, 0x1e, 0xca, 0x93, - 0x41, 0x6e, 0x3e, 0xb7, 0x4d, 0xd2, 0xf4, 0xaa, 0x1a, 0xfd, 0x66, 0xaf, 0xc3, 0x86, 0x6f, 0x8d, - 0xe6, 0x7e, 0x60, 0x3f, 0xb3, 0xf4, 0xc0, 0x76, 0x4f, 0x49, 0xc7, 0x2b, 0x69, 0xb5, 0x18, 0x3a, - 0xb0, 0xdd, 0x53, 0x14, 0x19, 0xd6, 0x79, 0x68, 0xf9, 0x2e, 0x8f, 0xf7, 0x7f, 0x5b, 0x16, 0x19, - 0x6d, 0x42, 0xe0, 0x3a, 0xd7, 0xc0, 0x8a, 0x7f, 0x2f, 0x8c, 0x6c, 0xc0, 0x47, 0xf6, 0xc1, 0x37, - 0x1a, 0xd9, 0x01, 0x8d, 0xec, 0x5d, 0x28, 0xd9, 0x6e, 0x68, 0xf9, 0xcf, 0x0c, 0xa7, 0xfe, 0xee, - 0x92, 0x34, 0x8e, 0x71, 0xec, 0x0e, 0x14, 0x03, 0xc7, 0xc6, 0xf5, 0x5e, 0x7f, 0x6f, 0x89, 0x2c, - 0x42, 0xb1, 0x7b, 0x50, 0x8e, 0xaf, 0xb5, 0xd5, 0xdf, 0x5f, 0xa2, 0x4b, 0x90, 0xec, 0x26, 0xe4, - 0xce, 0x70, 0x42, 0x3d, 0x5c, 0x3e, 0x2a, 0x40, 0x38, 0x6e, 0xdf, 0x63, 0xd4, 0xd9, 0x69, 0xfb, - 0x7e, 0xb4, 0xb4, 0x7d, 0xef, 0xd9, 0x8e, 0xc3, 0xb7, 0xef, 0xb1, 0xf8, 0x85, 0x9b, 0x1f, 0x71, - 0x60, 0x4b, 0x3e, 0x58, 0xde, 0xfc, 0x10, 0xf7, 0x84, 0x2e, 0x00, 0x56, 0x02, 0xf2, 0x7f, 0x73, - 0x37, 0xfe, 0x87, 0x72, 0x5f, 0xa5, 0x1d, 0xe3, 0x1a, 0x04, 0x71, 0x1a, 0x0d, 0x10, 0xe1, 0xfd, - 0x47, 0xa5, 0xe9, 0x23, 0x7e, 0x5b, 0x84, 0x43, 0x3a, 0xe6, 0x39, 0x7b, 0x0f, 0x6a, 0x51, 0x58, - 0x11, 0x16, 0x17, 0xd4, 0x3f, 0x5e, 0xaa, 0x41, 0x9a, 0x80, 0xb5, 0xa0, 0x3a, 0x46, 0xdd, 0x7d, - 0xca, 0x55, 0xf9, 0xfa, 0x27, 0x54, 0x91, 0x9d, 0x68, 0x63, 0x7d, 0x9e, 0xaa, 0xaf, 0xa5, 0xb8, - 0xd8, 0x03, 0x60, 0xf6, 0x98, 0x8f, 0xe7, 0x9e, 0xef, 0x4d, 0xb9, 0xba, 0x5e, 0xff, 0x94, 0x7b, - 0xd0, 0x96, 0x31, 0x74, 0x18, 0x68, 0xb9, 0xa6, 0x3e, 0x0d, 0x84, 0x9a, 0xf0, 0x3d, 0xaa, 0xa7, - 0x10, 0x5e, 0xf1, 0xf5, 0xd7, 0xc8, 0xd9, 0x8b, 0xb4, 0x07, 0x01, 0xd7, 0x1a, 0x3e, 0x05, 0x9c, - 0xae, 0xcf, 0x12, 0xd6, 0x5f, 0x7b, 0x21, 0x2b, 0xd2, 0x46, 0xac, 0x9f, 0xc0, 0x06, 0x77, 0x94, - 0x92, 0x46, 0x86, 0x53, 0xf4, 0xfb, 0xb2, 0xe4, 0x92, 0x5d, 0xc8, 0x5a, 0xd5, 0x94, 0x1d, 0xca, - 0x1f, 0xc3, 0x66, 0xe4, 0xeb, 0x0d, 0x85, 0x5b, 0xf8, 0xd7, 0xe5, 0x62, 0x63, 0x5f, 0xaa, 0x56, - 0x9b, 0x47, 0x3f, 0xa9, 0xc8, 0x47, 0x50, 0xa3, 0x5d, 0x34, 0xbe, 0x5f, 0xf4, 0x1b, 0x2b, 0xef, - 0x17, 0x55, 0x91, 0x28, 0x4a, 0xa1, 0xf0, 0x4f, 0xd6, 0xe9, 0x28, 0xb4, 0xea, 0x3f, 0xe0, 0xc2, - 0x3f, 0x06, 0x36, 0x43, 0x8b, 0x3d, 0x02, 0x30, 0x66, 0x33, 0xe7, 0x82, 0x4f, 0xcd, 0x1f, 0xd2, - 0xd4, 0xdc, 0x96, 0xa6, 0x66, 0x03, 0x91, 0x34, 0x37, 0xcb, 0x46, 0xf4, 0x93, 0x3d, 0x84, 0xea, - 0xcc, 0x0b, 0x42, 0xdd, 0x9c, 0x3a, 0xd4, 0xfe, 0x86, 0xbc, 0xb6, 0x0f, 0xbd, 0x20, 0x6c, 0x4d, - 0x1d, 0x6c, 0x05, 0xcc, 0xe2, 0xdf, 0xac, 0x0b, 0x97, 0x3c, 0x57, 0x37, 0xe7, 0x33, 0xc7, 0x1e, - 0x61, 0x0f, 0x18, 0x74, 0xf4, 0x5e, 0xdf, 0xa5, 0x12, 0x6f, 0x48, 0x25, 0xf6, 0xdd, 0x56, 0x44, - 0x24, 0x62, 0xdc, 0xb6, 0xbc, 0x45, 0x10, 0xd9, 0x99, 0x34, 0x06, 0x71, 0x4c, 0x68, 0x93, 0xab, - 0x06, 0x04, 0x8d, 0x82, 0x42, 0x3f, 0x81, 0xcd, 0x84, 0x0a, 0x1b, 0x18, 0xd4, 0x5b, 0xf2, 0x4c, - 0x96, 0x82, 0xcc, 0x6b, 0x11, 0x23, 0xc2, 0x02, 0xea, 0x3b, 0xcf, 0x71, 0xe6, 0x33, 0x21, 0x4a, - 0xeb, 0x6d, 0xd1, 0x77, 0x04, 0xe4, 0x52, 0x52, 0x32, 0xc5, 0xad, 0x69, 0x7d, 0x4f, 0x36, 0xc5, - 0xad, 0x29, 0xaa, 0x19, 0x22, 0xe0, 0xf7, 0x99, 0x6d, 0x9d, 0x05, 0xf5, 0xc7, 0x14, 0xab, 0x28, - 0x42, 0xaa, 0x9f, 0x20, 0x08, 0xf7, 0x7d, 0xd3, 0xf6, 0xd1, 0x04, 0xa0, 0xe0, 0xac, 0x7d, 0x1e, - 0x93, 0xcb, 0x41, 0x48, 0xc1, 0x9a, 0x50, 0x27, 0x71, 0x2e, 0xd6, 0x6a, 0xea, 0x0c, 0xae, 0xb3, - 0xb4, 0x24, 0x2f, 0x23, 0x2d, 0x3f, 0xe2, 0x3b, 0x94, 0x4e, 0xe3, 0x7e, 0x08, 0xaf, 0xae, 0xce, - 0x24, 0xba, 0x7e, 0xf9, 0x19, 0xe9, 0x1b, 0xd7, 0x56, 0x70, 0x37, 0xe9, 0x46, 0xa6, 0xfa, 0xb3, - 0x3c, 0x94, 0x22, 0xd3, 0x88, 0x55, 0xa0, 0x78, 0xd4, 0xfb, 0xbc, 0xd7, 0x7f, 0xda, 0xe3, 0xb7, - 0x10, 0x1b, 0x83, 0x41, 0x5b, 0x1b, 0x2a, 0x26, 0xdb, 0x00, 0xa0, 0x9b, 0x3d, 0xfa, 0xa0, 0xd9, - 0xe8, 0xf1, 0x5b, 0x89, 0x74, 0x9f, 0x88, 0xa7, 0xd7, 0xd9, 0x16, 0xd4, 0xf6, 0x8e, 0x7a, 0x14, - 0x57, 0xc8, 0x41, 0x59, 0x04, 0xb5, 0xbf, 0xe0, 0x27, 0x9c, 0x1c, 0x94, 0x43, 0xd0, 0x41, 0x63, - 0xd8, 0xd6, 0x3a, 0x11, 0x28, 0x4f, 0x21, 0x8a, 0xfd, 0x23, 0xad, 0x29, 0x72, 0x2a, 0xb0, 0xcb, - 0xb0, 0x15, 0xb3, 0x45, 0x59, 0x2a, 0x45, 0xac, 0xd9, 0xa1, 0xd6, 0xff, 0xac, 0xdd, 0x1c, 0x2a, - 0x40, 0xc7, 0xa5, 0x8f, 0x1f, 0x2b, 0x15, 0x56, 0x85, 0x52, 0xab, 0x33, 0x18, 0x76, 0x7a, 0xcd, - 0xa1, 0x52, 0xc5, 0x0a, 0xef, 0x75, 0xba, 0xc3, 0xb6, 0xa6, 0xd4, 0x58, 0x09, 0x72, 0x9f, 0xf5, - 0x3b, 0x3d, 0x65, 0x83, 0x6e, 0x38, 0x35, 0x0e, 0x0e, 0xbb, 0x6d, 0x65, 0x13, 0xa1, 0x83, 0xbe, - 0x36, 0x54, 0x14, 0x84, 0x3e, 0xed, 0xf4, 0x5a, 0xfd, 0xa7, 0xca, 0x16, 0x2b, 0x43, 0xfe, 0xa8, - 0x87, 0xc5, 0x30, 0x56, 0x83, 0x32, 0xfd, 0xd4, 0x1b, 0xdd, 0xae, 0x72, 0x49, 0x3a, 0x63, 0xdd, - 0x46, 0x14, 0x9d, 0xd8, 0x0e, 0xb0, 0x0e, 0x97, 0xb1, 0x2d, 0x71, 0x92, 0xa8, 0xaf, 0x60, 0x3e, - 0x07, 0x9d, 0xde, 0xd1, 0x40, 0xb9, 0x8a, 0xc4, 0xf4, 0x93, 0x30, 0x75, 0xcc, 0xa7, 0xd3, 0xa3, - 0xae, 0xbc, 0x89, 0xbf, 0x5b, 0xed, 0x6e, 0x7b, 0xd8, 0x56, 0x6e, 0x61, 0xab, 0xba, 0xfd, 0xe6, - 0xe7, 0x7a, 0xff, 0x50, 0x79, 0x0d, 0xfb, 0xf4, 0x50, 0x6b, 0xeb, 0x82, 0xf0, 0x36, 0x53, 0xa0, - 0xba, 0x77, 0xf4, 0xe3, 0x1f, 0x7f, 0xa9, 0x8b, 0x46, 0xbd, 0x8e, 0x65, 0x26, 0x14, 0xfa, 0xd1, - 0xe7, 0xca, 0xdd, 0x05, 0xd0, 0xe0, 0x73, 0xe5, 0x0d, 0xec, 0x94, 0xa8, 0x97, 0x95, 0x7b, 0x48, - 0xa0, 0xb5, 0x9b, 0x47, 0xda, 0xa0, 0xf3, 0xa4, 0xad, 0x37, 0x87, 0x6d, 0xe5, 0x4d, 0xea, 0x85, - 0x4e, 0xef, 0x73, 0xe5, 0x3e, 0x56, 0x13, 0x7f, 0xf1, 0xbe, 0x7f, 0x8b, 0x31, 0xd8, 0x48, 0x68, - 0x09, 0xf6, 0x36, 0x92, 0xec, 0x6a, 0xfd, 0x46, 0xab, 0xd9, 0x18, 0x0c, 0x95, 0x77, 0xb0, 0x8d, - 0x83, 0xc3, 0x6e, 0x67, 0xa8, 0x3c, 0xc0, 0x86, 0x3c, 0x6e, 0x0c, 0xf7, 0xdb, 0x9a, 0xf2, 0x2e, - 0x0e, 0xe3, 0xb0, 0x73, 0xd0, 0xd6, 0x45, 0x9f, 0x3e, 0xc4, 0x32, 0xf6, 0x3a, 0xdd, 0xae, 0xf2, - 0x88, 0xce, 0x08, 0x1b, 0xda, 0xb0, 0x43, 0x03, 0xf9, 0x01, 0x66, 0xd0, 0x38, 0x3c, 0xec, 0x7e, - 0xa9, 0x7c, 0x88, 0x0d, 0x3c, 0x38, 0xea, 0x0e, 0x3b, 0xfa, 0xd1, 0x61, 0xab, 0x31, 0x6c, 0x2b, - 0x1f, 0xd1, 0x28, 0xf7, 0x07, 0xc3, 0xd6, 0x41, 0x57, 0xf9, 0x98, 0xf2, 0xa4, 0x39, 0xd6, 0xec, - 0xf6, 0x7b, 0x6d, 0xe5, 0x13, 0x35, 0x57, 0xda, 0x51, 0x76, 0xd4, 0x5c, 0x49, 0x55, 0x54, 0xf5, - 0xb7, 0xa1, 0x14, 0xd9, 0xc5, 0x98, 0x65, 0xa7, 0xd7, 0x6b, 0x6b, 0xca, 0x1a, 0x16, 0xdb, 0x6d, - 0xef, 0x0d, 0x95, 0x0c, 0x1d, 0x9e, 0x76, 0x1e, 0xef, 0x0f, 0x95, 0x75, 0xfc, 0xd9, 0x3f, 0xc2, - 0x1e, 0xcc, 0x52, 0xd3, 0xdb, 0x07, 0x1d, 0x25, 0x87, 0xbf, 0x1a, 0xbd, 0x61, 0x47, 0xc9, 0xd3, - 0x04, 0xe9, 0xf4, 0x1e, 0x77, 0xdb, 0x4a, 0x01, 0xa1, 0x07, 0x0d, 0xed, 0x73, 0xa5, 0xc8, 0x33, - 0x6d, 0xb5, 0xbf, 0x50, 0x4a, 0xac, 0x00, 0xeb, 0xdd, 0x87, 0x4a, 0x19, 0x41, 0xad, 0x76, 0xeb, - 0xe8, 0x50, 0x01, 0xf5, 0x1e, 0x14, 0x1b, 0x93, 0xc9, 0x81, 0x67, 0xd2, 0x79, 0xed, 0xde, 0x51, - 0xb7, 0xcb, 0x17, 0xcc, 0x6e, 0x7f, 0x38, 0xec, 0x1f, 0x28, 0x19, 0x9c, 0xa2, 0xc3, 0xfe, 0xa1, - 0xb2, 0xae, 0x76, 0xa0, 0x14, 0x69, 0x00, 0xd2, 0xf5, 0xbc, 0x12, 0xe4, 0x0e, 0xb5, 0xf6, 0x13, - 0x7e, 0xe2, 0xdf, 0x6b, 0x7f, 0x81, 0xd5, 0xc4, 0x5f, 0x98, 0x51, 0x16, 0x0b, 0xe2, 0xf7, 0xe8, - 0xe8, 0x7e, 0x5e, 0xb7, 0xd3, 0x6b, 0x37, 0x34, 0x25, 0xaf, 0x7e, 0x08, 0x5b, 0x4b, 0xf2, 0x93, - 0x8a, 0x6f, 0x74, 0x44, 0xf1, 0x9d, 0xc7, 0xbd, 0xbe, 0xd6, 0xe6, 0x17, 0xfe, 0x44, 0xa7, 0xae, - 0xab, 0x6f, 0x41, 0x39, 0x16, 0xf4, 0x38, 0xc9, 0x9a, 0x5a, 0x7f, 0x30, 0xe0, 0x63, 0xb0, 0x86, - 0x69, 0xea, 0x1b, 0x9e, 0xce, 0x7c, 0x96, 0x2b, 0xdd, 0x52, 0x76, 0x3e, 0xcb, 0x95, 0xee, 0x28, - 0xaf, 0xab, 0x03, 0xd8, 0x8a, 0xf6, 0x1b, 0xba, 0x6c, 0x40, 0x86, 0xd0, 0x36, 0xe4, 0xbb, 0xd6, - 0x33, 0xcb, 0x89, 0xa2, 0xe6, 0x29, 0x81, 0xd0, 0xfe, 0xf1, 0x57, 0x9d, 0xf8, 0xb6, 0x38, 0x25, - 0x50, 0xc1, 0xec, 0x49, 0x17, 0xd6, 0xe9, 0x8e, 0xe5, 0xff, 0x9b, 0x81, 0x52, 0xbc, 0x8b, 0xdd, - 0x81, 0xf5, 0xe1, 0x40, 0x1c, 0x59, 0x6d, 0x3f, 0x48, 0xde, 0xe7, 0x18, 0x46, 0xbf, 0xb4, 0xf5, - 0xe1, 0x80, 0xbd, 0x0d, 0x05, 0x7e, 0x67, 0x56, 0xb8, 0xab, 0xb6, 0xd3, 0x3b, 0xe3, 0x90, 0x70, - 0x9a, 0xa0, 0x61, 0x1f, 0x42, 0x39, 0xae, 0xad, 0xf0, 0xdf, 0x5c, 0x4d, 0x33, 0xc4, 0x68, 0x2d, - 0xa1, 0x54, 0xbb, 0xb0, 0x91, 0xce, 0x90, 0xdd, 0x04, 0xe0, 0x59, 0x4a, 0x4e, 0x4a, 0x09, 0xc2, - 0xae, 0x43, 0x74, 0x95, 0xb7, 0x45, 0x15, 0xab, 0xc5, 0x57, 0x7b, 0x5b, 0xea, 0x3f, 0xcf, 0x02, - 0x24, 0x7a, 0x30, 0x76, 0x44, 0xec, 0x95, 0xca, 0x8b, 0x33, 0xf9, 0x57, 0xa0, 0xec, 0x78, 0x86, - 0x29, 0x3f, 0xcf, 0x51, 0x42, 0x00, 0x0d, 0x93, 0x7c, 0xe9, 0xac, 0xcc, 0x03, 0x62, 0xd8, 0x15, - 0x28, 0x8c, 0x3d, 0x7f, 0x6a, 0x84, 0xe2, 0x8a, 0x84, 0x48, 0xe1, 0x76, 0x66, 0x4f, 0x5c, 0xcf, - 0xb7, 0xd0, 0x1a, 0x70, 0xe9, 0x96, 0x04, 0x8e, 0x41, 0x55, 0x00, 0xbb, 0x08, 0xc3, 0xed, 0xc8, - 0x72, 0x47, 0x8e, 0x17, 0x58, 0x26, 0x6e, 0x30, 0x05, 0x52, 0xf9, 0x21, 0x02, 0xed, 0x5e, 0xf0, - 0xd6, 0xfa, 0x53, 0xdb, 0x35, 0x42, 0x71, 0x4c, 0x45, 0xad, 0x8d, 0x20, 0x58, 0xdd, 0xaf, 0x02, - 0x4f, 0x38, 0xa9, 0xf8, 0x0d, 0xab, 0x12, 0x02, 0xa8, 0xba, 0xaf, 0x02, 0x58, 0xc1, 0xc8, 0x98, - 0xf1, 0xcc, 0xcb, 0xfc, 0x9a, 0xab, 0x80, 0xec, 0x5e, 0xb0, 0x2e, 0x6c, 0x0c, 0x8f, 0x71, 0xfb, - 0xf5, 0x5a, 0x46, 0x68, 0x34, 0x3d, 0x47, 0xb8, 0x8f, 0xee, 0x2c, 0x1a, 0x0c, 0x0f, 0xd2, 0x64, - 0x3c, 0x32, 0x74, 0x81, 0x17, 0x0d, 0x78, 0x7b, 0x64, 0x1d, 0x5b, 0xfe, 0x84, 0x1b, 0x1f, 0x95, - 0xd4, 0xdd, 0x06, 0x8e, 0x21, 0xeb, 0xa3, 0x62, 0x27, 0x89, 0xeb, 0x0d, 0xb8, 0xb4, 0x22, 0xf3, - 0x6f, 0x15, 0x5a, 0xf8, 0x8f, 0xd7, 0xa1, 0x22, 0xe5, 0x4f, 0xe7, 0x04, 0xc6, 0x8c, 0xfc, 0x5c, - 0x71, 0x6c, 0x75, 0x59, 0x40, 0xf8, 0x4d, 0x92, 0x91, 0x11, 0x1a, 0x8e, 0x37, 0x91, 0xee, 0xc8, - 0x08, 0x48, 0xc7, 0xa4, 0xa3, 0x3b, 0x63, 0x6a, 0x05, 0x33, 0x63, 0x14, 0xad, 0x90, 0x04, 0x90, - 0xbc, 0x0b, 0x93, 0x93, 0xdf, 0x85, 0x51, 0xf8, 0x19, 0x2f, 0x0f, 0x92, 0xa1, 0x23, 0xdd, 0x5b, - 0x50, 0x89, 0xf4, 0x46, 0x2c, 0x45, 0x1c, 0x43, 0x46, 0xa0, 0x8e, 0xc9, 0xee, 0x42, 0xf2, 0xee, - 0x8d, 0x6e, 0x04, 0xba, 0x37, 0x8e, 0x22, 0xf4, 0x62, 0x70, 0x23, 0xe8, 0x8f, 0xe3, 0xb3, 0x81, - 0xa9, 0x67, 0xc6, 0xe3, 0x8b, 0x00, 0x92, 0x71, 0x0f, 0xe0, 0x92, 0x70, 0x58, 0x5a, 0xa6, 0x3e, - 0xb6, 0x2d, 0xc7, 0xd4, 0x6d, 0x93, 0x9f, 0xa0, 0xe7, 0xb5, 0xad, 0x18, 0xb5, 0x87, 0x98, 0x8e, - 0x49, 0x1a, 0x96, 0xb0, 0x52, 0x4d, 0x7b, 0x62, 0x05, 0xa1, 0x88, 0x27, 0xab, 0x72, 0x60, 0x8b, - 0x60, 0xaa, 0x13, 0x2d, 0x91, 0x46, 0x18, 0xd2, 0x2d, 0xb6, 0x58, 0xdd, 0xcb, 0x44, 0x17, 0x5c, - 0xb8, 0xa6, 0xf7, 0x0a, 0xc5, 0x62, 0x89, 0x80, 0x62, 0xb1, 0x52, 0xe2, 0x40, 0xe1, 0xbb, 0xb0, - 0x89, 0x48, 0x51, 0x29, 0x22, 0xe1, 0x37, 0x9b, 0x6a, 0x23, 0xcf, 0xe1, 0x15, 0x42, 0xa0, 0xfa, - 0xff, 0xe7, 0x01, 0x12, 0x43, 0x9f, 0x2e, 0xcd, 0x91, 0x07, 0x2e, 0x1e, 0xb9, 0x22, 0xa5, 0x3b, - 0x26, 0x7b, 0x08, 0x57, 0xc4, 0x15, 0xbd, 0x38, 0xc8, 0xc3, 0x76, 0xf5, 0x63, 0x23, 0x0a, 0x2a, - 0x63, 0x02, 0xcb, 0x8f, 0xef, 0x3b, 0xee, 0xae, 0x81, 0x66, 0xc3, 0xa6, 0xcc, 0x13, 0x5e, 0xcc, - 0xd2, 0x1e, 0x7f, 0x59, 0x19, 0x4d, 0xd8, 0x87, 0x17, 0x33, 0xf6, 0x1e, 0x5c, 0xf6, 0xad, 0xb1, - 0x6f, 0x05, 0x27, 0x7a, 0x18, 0xc8, 0x85, 0xf1, 0xa8, 0xcf, 0x2d, 0x81, 0x1c, 0x06, 0x71, 0x59, - 0xef, 0xc1, 0x65, 0xd1, 0xb9, 0x0b, 0xd5, 0xe3, 0x9e, 0xf4, 0x2d, 0x8e, 0x94, 0x6b, 0x47, 0xa1, - 0xc0, 0xe4, 0xfd, 0x88, 0x5e, 0x1a, 0x2a, 0x69, 0x65, 0xee, 0xe9, 0x10, 0x77, 0xea, 0xc9, 0x85, - 0x41, 0x0b, 0xb7, 0xa4, 0xf1, 0x04, 0x53, 0x21, 0x87, 0x63, 0x4f, 0x43, 0xb7, 0xf1, 0x70, 0xe3, - 0x01, 0xbd, 0xa4, 0x44, 0xef, 0x21, 0x78, 0xa6, 0xa5, 0x11, 0x8e, 0xbd, 0x83, 0xf3, 0x22, 0x69, - 0x76, 0xf4, 0x18, 0x08, 0x8f, 0x73, 0x50, 0x92, 0x86, 0x6a, 0xfc, 0x59, 0x90, 0xb7, 0x80, 0x49, - 0x35, 0x8f, 0xa8, 0xab, 0x44, 0xbd, 0x19, 0x57, 0x5b, 0x10, 0xbf, 0x01, 0x54, 0x45, 0x7e, 0x8c, - 0x56, 0x5b, 0xb6, 0xf7, 0x11, 0x49, 0x27, 0x6a, 0xef, 0xc1, 0xe5, 0xa4, 0x75, 0xba, 0x11, 0xea, - 0xe1, 0x89, 0xa5, 0x5b, 0xae, 0x49, 0xf7, 0x2a, 0x4b, 0xda, 0x56, 0xdc, 0xd0, 0x46, 0x38, 0x3c, - 0xb1, 0xd0, 0x62, 0x97, 0x3c, 0xaa, 0x9b, 0x2f, 0xf6, 0xa8, 0x7e, 0x04, 0xf5, 0xb4, 0x4e, 0x2d, - 0x75, 0x37, 0xbf, 0x97, 0xbc, 0x2d, 0xc7, 0x8a, 0xc4, 0x3d, 0x7e, 0x1f, 0xb6, 0x4e, 0x8c, 0x20, - 0xad, 0x8f, 0x93, 0xa3, 0xb7, 0xa4, 0x6d, 0x9e, 0x18, 0x81, 0xac, 0x84, 0xab, 0x7f, 0x90, 0x81, - 0x8d, 0xb4, 0xeb, 0x83, 0x5f, 0xf6, 0xe2, 0xd7, 0xa0, 0x32, 0xb4, 0xc6, 0xa2, 0x24, 0xae, 0x05, - 0x1e, 0x50, 0x32, 0x9f, 0xba, 0xd1, 0x5a, 0x98, 0x9d, 0xf2, 0xcb, 0x51, 0xec, 0x4d, 0x28, 0xce, - 0x4e, 0xb9, 0x84, 0x7e, 0xde, 0xec, 0x2b, 0xcc, 0x78, 0x20, 0xfc, 0x9b, 0x50, 0x9c, 0x9f, 0x26, - 0x2f, 0x0e, 0xac, 0x24, 0x9d, 0x13, 0xa9, 0xfa, 0x4f, 0xd6, 0xa1, 0x2a, 0x7b, 0xec, 0xbe, 0x49, - 0xa4, 0xc9, 0xb7, 0x0a, 0x02, 0xda, 0xa1, 0x58, 0x55, 0x9d, 0x22, 0xcd, 0xb1, 0x9f, 0x78, 0x98, - 0x09, 0x9c, 0x18, 0x41, 0x63, 0x1e, 0x7a, 0x28, 0xf2, 0x51, 0x94, 0x7a, 0x4e, 0x14, 0x8d, 0xcf, - 0x57, 0x06, 0xca, 0x04, 0x11, 0x88, 0xff, 0x9e, 0xb8, 0xec, 0x43, 0x37, 0x01, 0x29, 0xac, 0x33, - 0xbf, 0x34, 0x5f, 0xaa, 0xd1, 0x45, 0x40, 0x0a, 0x63, 0x7d, 0x08, 0x9b, 0xc9, 0xd5, 0x0a, 0xce, - 0x52, 0x58, 0x62, 0xa9, 0xc5, 0xf7, 0x2a, 0xc4, 0x2b, 0x05, 0x35, 0x3b, 0xd0, 0x3d, 0xc7, 0x8c, - 0xee, 0x70, 0x15, 0xa3, 0xf3, 0x94, 0xbe, 0x63, 0x8a, 0xeb, 0xa7, 0x9c, 0xc6, 0xb5, 0xce, 0x22, - 0x9a, 0xf8, 0xcc, 0xa5, 0x67, 0x9d, 0x89, 0xbb, 0x5c, 0x7f, 0x96, 0x81, 0xad, 0x25, 0x27, 0x1d, - 0x8a, 0xf6, 0xe4, 0x05, 0x2f, 0xfc, 0x89, 0x46, 0xe6, 0xd4, 0x08, 0x47, 0x27, 0xfa, 0xcc, 0xb7, - 0xc6, 0xf6, 0x79, 0xf4, 0x0c, 0x19, 0xc1, 0x0e, 0x09, 0x84, 0xd2, 0x9f, 0x9f, 0x1b, 0xf2, 0x63, - 0x0c, 0x2e, 0xf8, 0xf8, 0x59, 0x61, 0x97, 0xce, 0x2f, 0xa2, 0x30, 0xd7, 0xdc, 0x73, 0xc2, 0x5c, - 0xaf, 0x43, 0xd9, 0xf5, 0x28, 0xa4, 0x69, 0x76, 0x2a, 0x62, 0xc1, 0x8a, 0xae, 0x17, 0xf6, 0xdd, - 0xc3, 0x53, 0xf6, 0x10, 0x2e, 0xcf, 0x03, 0x8a, 0x1b, 0x38, 0xb6, 0xfc, 0xe0, 0xc4, 0x8e, 0xcd, - 0x65, 0x2e, 0x40, 0x2e, 0xcd, 0x03, 0xeb, 0x20, 0xc6, 0xf1, 0x96, 0xa8, 0x37, 0xa0, 0xd0, 0x89, - 0x9d, 0x8b, 0x71, 0x5c, 0x5d, 0x56, 0xbc, 0xdd, 0xe3, 0x41, 0x99, 0x5b, 0x9d, 0x07, 0xc6, 0x8c, - 0xdd, 0x87, 0xec, 0xd4, 0x98, 0x89, 0xc3, 0xca, 0x7a, 0x7c, 0xa2, 0xcb, 0xb1, 0x0f, 0x0e, 0x8c, - 0x19, 0xdf, 0xfd, 0x91, 0xe8, 0xfa, 0x47, 0x50, 0x8a, 0x00, 0xdf, 0x6a, 0xc7, 0xfe, 0x57, 0xeb, - 0x50, 0x6e, 0xc9, 0x87, 0x03, 0x23, 0xc3, 0xd5, 0x43, 0x7f, 0xee, 0xa2, 0x02, 0x1d, 0xbd, 0x52, - 0x32, 0x32, 0xdc, 0xa1, 0x00, 0x45, 0x53, 0x7b, 0xfd, 0x05, 0x53, 0xfb, 0x06, 0x80, 0x4f, 0xbe, - 0x35, 0x72, 0xaf, 0x65, 0xe3, 0x38, 0xe3, 0x8e, 0xd9, 0x31, 0xcf, 0x57, 0x87, 0x58, 0xe5, 0xbe, - 0x79, 0x88, 0x55, 0x7e, 0x65, 0x88, 0xd5, 0xdd, 0x64, 0x7b, 0xc1, 0x29, 0x8e, 0x05, 0x97, 0xf9, - 0x26, 0x37, 0x8b, 0xaf, 0x36, 0x61, 0xe9, 0xdf, 0x83, 0x8d, 0xa8, 0x75, 0x22, 0x3f, 0x48, 0xdd, - 0xa6, 0x12, 0x38, 0x7e, 0x9a, 0x50, 0x0b, 0xe5, 0x64, 0x7a, 0xc9, 0x56, 0x5e, 0x12, 0xc6, 0xf5, - 0x97, 0x33, 0xc0, 0x84, 0x2f, 0x68, 0x6f, 0xee, 0x38, 0x43, 0xeb, 0x9c, 0x24, 0xc3, 0x7d, 0xd8, - 0x12, 0x87, 0x1d, 0xd2, 0x85, 0x57, 0x71, 0xbc, 0xcf, 0x11, 0xc9, 0xf1, 0xfe, 0xaa, 0xbb, 0xb1, - 0xeb, 0x2b, 0xef, 0xc6, 0xae, 0xbe, 0x73, 0x7b, 0x0b, 0x2a, 0xf2, 0xcd, 0x52, 0xae, 0x27, 0x81, - 0x11, 0x5f, 0x2a, 0x55, 0xff, 0x6c, 0x1d, 0x20, 0xf1, 0x57, 0xfd, 0xaa, 0xe3, 0xe3, 0x56, 0x0c, - 0x49, 0x76, 0xd5, 0x90, 0xdc, 0x03, 0x45, 0xa6, 0x93, 0xae, 0x38, 0x6f, 0x24, 0x84, 0x91, 0x9a, - 0x63, 0x07, 0xf2, 0xdd, 0x52, 0xba, 0x7e, 0x22, 0x42, 0x8a, 0x38, 0x92, 0x3b, 0xcf, 0xc5, 0x02, - 0x2c, 0xd9, 0x01, 0x97, 0xc9, 0xec, 0x53, 0xb8, 0x16, 0x73, 0xea, 0x67, 0x76, 0x78, 0xe2, 0xcd, - 0x43, 0xb1, 0x58, 0x03, 0x21, 0xa5, 0xae, 0x44, 0x39, 0x3d, 0xe5, 0x68, 0xbe, 0x5e, 0x03, 0xb4, - 0x96, 0xc6, 0x73, 0xc7, 0xd1, 0x43, 0xeb, 0x3c, 0x14, 0xcf, 0x92, 0xd4, 0x53, 0xae, 0x3e, 0x69, - 0x78, 0xb5, 0xd2, 0x58, 0x24, 0xd4, 0xbf, 0x97, 0x83, 0xfc, 0x8f, 0xe6, 0x96, 0x7f, 0xc1, 0x3e, - 0x82, 0x72, 0x10, 0x4e, 0x43, 0xf9, 0xd4, 0x5d, 0x3c, 0x5e, 0x43, 0x78, 0x3a, 0x34, 0xb7, 0xa6, - 0x96, 0x1b, 0x72, 0x1f, 0x38, 0xd2, 0xd2, 0x06, 0xb4, 0x0d, 0xf9, 0x20, 0xb4, 0x66, 0x81, 0x88, - 0x9d, 0xe5, 0x09, 0xb6, 0x03, 0x79, 0xd7, 0x33, 0xad, 0x20, 0x1d, 0x21, 0xdb, 0x43, 0x8d, 0x83, - 0x23, 0x98, 0x0a, 0x85, 0x78, 0xc4, 0x97, 0x4e, 0xbe, 0x39, 0x86, 0xee, 0xdd, 0x58, 0x86, 0x69, - 0xbb, 0x93, 0xe8, 0xca, 0x78, 0x9c, 0xc6, 0xad, 0x95, 0xac, 0x2c, 0x63, 0x12, 0x3d, 0x35, 0x21, - 0x92, 0x6c, 0x07, 0x2a, 0xf8, 0xf3, 0xa9, 0x6f, 0x87, 0xd6, 0xe0, 0x51, 0x24, 0xdd, 0x25, 0x10, - 0xda, 0x48, 0xa6, 0x15, 0x5a, 0xa3, 0x70, 0xf0, 0xb5, 0x88, 0x3b, 0x2d, 0x6b, 0x12, 0x84, 0x7d, - 0x0f, 0xd8, 0xb1, 0x31, 0x3a, 0x9d, 0xf8, 0x14, 0x2d, 0xf2, 0xf5, 0xdc, 0xf2, 0x6d, 0x2b, 0x8a, - 0x33, 0xad, 0x48, 0x9d, 0xa2, 0x6d, 0x25, 0x64, 0x3f, 0xe2, 0x54, 0x68, 0xdd, 0x4d, 0x8d, 0xf3, - 0x96, 0x37, 0x13, 0x61, 0x7c, 0x22, 0xc5, 0x1e, 0xc1, 0x15, 0xdc, 0x1c, 0xa3, 0x8b, 0xc9, 0x38, - 0x87, 0x84, 0x77, 0x95, 0xbf, 0x52, 0x75, 0xe9, 0xc4, 0x08, 0x92, 0xbb, 0x88, 0xc2, 0x29, 0xb0, - 0x0b, 0xdb, 0x91, 0xe9, 0x61, 0x5a, 0x33, 0xcb, 0x35, 0x2d, 0x77, 0x64, 0xc7, 0x37, 0x1d, 0x97, - 0x26, 0xff, 0x25, 0x41, 0xdc, 0x92, 0x68, 0xd5, 0xdf, 0x82, 0x5a, 0x6a, 0xec, 0x96, 0x9c, 0x83, - 0x83, 0x76, 0xb7, 0xdd, 0x1c, 0x72, 0x67, 0x83, 0x70, 0x5a, 0xad, 0x4b, 0xde, 0xad, 0x9c, 0xe4, - 0x84, 0xc8, 0x93, 0x6f, 0xac, 0xad, 0x3d, 0x6e, 0x2b, 0x05, 0x35, 0x57, 0xca, 0x2a, 0x59, 0xf5, - 0xaf, 0xac, 0xc3, 0xd6, 0xd0, 0x37, 0xdc, 0xc0, 0xe0, 0x7a, 0x90, 0x1b, 0xfa, 0x9e, 0xc3, 0xbe, - 0x07, 0xa5, 0x70, 0xe4, 0xc8, 0x93, 0xe9, 0x56, 0x24, 0xba, 0x16, 0x48, 0x1f, 0x0c, 0x47, 0xfc, - 0x58, 0xa5, 0x18, 0xf2, 0x1f, 0xec, 0x1d, 0xc8, 0x1f, 0x5b, 0x13, 0xdb, 0x15, 0xd2, 0xfb, 0xf2, - 0x22, 0xe3, 0x2e, 0x22, 0xf7, 0xd7, 0x34, 0x4e, 0xc5, 0xde, 0x83, 0xc2, 0xc8, 0x9b, 0x46, 0xdb, - 0x66, 0x72, 0xb3, 0x51, 0x2a, 0x08, 0xb1, 0xfb, 0x6b, 0x9a, 0xa0, 0x63, 0x1f, 0x41, 0xc9, 0xf7, - 0x1c, 0x07, 0xc7, 0x4e, 0x6c, 0xa8, 0xf5, 0x45, 0x1e, 0x4d, 0xe0, 0xf7, 0xd7, 0xb4, 0x98, 0x56, - 0x7d, 0x00, 0x45, 0x51, 0x59, 0xec, 0x86, 0xdd, 0xf6, 0xe3, 0x8e, 0xe8, 0xc1, 0x66, 0xff, 0xe0, - 0xa0, 0x33, 0xe4, 0xd7, 0xb6, 0xb5, 0x7e, 0xb7, 0xbb, 0xdb, 0x68, 0x7e, 0xae, 0xac, 0xef, 0x96, - 0xa0, 0xc0, 0x87, 0x58, 0xfd, 0x9d, 0x0c, 0x6c, 0x2e, 0x34, 0x80, 0x7d, 0x02, 0x39, 0xb2, 0xd1, - 0x78, 0xf7, 0xdc, 0x59, 0xd9, 0x4a, 0x29, 0xcd, 0xb5, 0x75, 0xe4, 0x50, 0x3f, 0x85, 0x8d, 0x34, - 0x5c, 0x72, 0x48, 0xd5, 0xa0, 0xac, 0xb5, 0x1b, 0x2d, 0xbd, 0xdf, 0xeb, 0x7e, 0xc9, 0xfd, 0xbb, - 0x94, 0x7c, 0xaa, 0x75, 0xc8, 0x87, 0xf4, 0x9b, 0xa0, 0x2c, 0x76, 0x0c, 0x7b, 0x8c, 0x96, 0xd7, - 0x74, 0xe6, 0x58, 0xa4, 0xe0, 0x4a, 0x43, 0x76, 0x73, 0x45, 0x4f, 0x0a, 0x32, 0x1e, 0x2a, 0x35, - 0x4a, 0xa5, 0xd5, 0xdf, 0x02, 0xb6, 0xdc, 0x83, 0xbf, 0xba, 0xec, 0xff, 0x5b, 0x06, 0x72, 0x87, - 0x8e, 0xe1, 0xb2, 0xdb, 0x90, 0xa7, 0x47, 0x94, 0xc4, 0x1e, 0x20, 0xaf, 0x48, 0x9c, 0x16, 0x84, - 0x63, 0x6f, 0x41, 0x36, 0x1c, 0x45, 0xb7, 0xc5, 0xaf, 0x3e, 0x67, 0xf2, 0xed, 0xaf, 0x69, 0x48, - 0xc5, 0xee, 0x41, 0xd6, 0x34, 0xa3, 0x9b, 0x13, 0xc2, 0x2d, 0xd5, 0x32, 0x42, 0xa3, 0x65, 0x8d, - 0x6d, 0xd7, 0x16, 0x8f, 0x3e, 0x21, 0x09, 0x7b, 0x1d, 0xb2, 0xe6, 0xc8, 0x49, 0x5f, 0x83, 0xe1, - 0x8e, 0x88, 0x38, 0x43, 0x73, 0xe4, 0xa0, 0xfe, 0x18, 0xfa, 0x17, 0xba, 0x3f, 0x77, 0x29, 0xe6, - 0x36, 0x10, 0x36, 0x5d, 0x05, 0x35, 0xa1, 0x39, 0x05, 0xee, 0x06, 0xe2, 0xda, 0xe9, 0xcc, 0xb7, - 0x66, 0x86, 0x1f, 0x5b, 0x73, 0x76, 0x70, 0xc8, 0x01, 0xbb, 0x05, 0xa0, 0x97, 0x72, 0xd5, 0xb7, - 0xe9, 0x85, 0x1f, 0x34, 0x0b, 0xd4, 0xe8, 0xd7, 0x8a, 0x47, 0x15, 0x05, 0x46, 0xfd, 0xf3, 0x2c, - 0x54, 0xa4, 0xfa, 0xb0, 0x0f, 0xa0, 0x64, 0xa6, 0x17, 0xe2, 0xb5, 0xa5, 0x4a, 0x3f, 0x68, 0x45, - 0x4b, 0xd0, 0x14, 0xd3, 0x9b, 0xce, 0xec, 0x42, 0xfd, 0x99, 0xe1, 0xdb, 0xfc, 0xe5, 0xb8, 0x75, - 0xf9, 0xf0, 0x6c, 0x60, 0x85, 0x4f, 0x22, 0xcc, 0xfe, 0x9a, 0x56, 0x0d, 0xa4, 0x34, 0xd9, 0x2e, - 0xa2, 0x49, 0xd9, 0xd4, 0x6b, 0x7d, 0x1c, 0xb8, 0xbf, 0xa6, 0x45, 0x78, 0x24, 0xb5, 0xce, 0xad, - 0xd1, 0x3c, 0x8c, 0x6c, 0x97, 0x5a, 0xd4, 0x20, 0x02, 0xd2, 0x93, 0xa1, 0xfc, 0x27, 0x7b, 0x88, - 0x12, 0xdb, 0x70, 0x1c, 0x8f, 0x14, 0xbe, 0xbc, 0x7c, 0x94, 0xd5, 0x8a, 0xe1, 0xfc, 0x89, 0xd2, - 0x28, 0xc5, 0xee, 0x42, 0xde, 0x0b, 0x4f, 0xac, 0xc8, 0x22, 0x88, 0xde, 0x0a, 0x42, 0x50, 0xab, - 0xd9, 0xc5, 0x99, 0x42, 0x68, 0xf5, 0x0f, 0x33, 0x50, 0x14, 0x3d, 0xc0, 0xb6, 0xa0, 0x36, 0x68, - 0x0f, 0xf5, 0x27, 0x0d, 0xad, 0xd3, 0xd8, 0xed, 0xb6, 0xc5, 0xed, 0x9d, 0xc7, 0x5a, 0xa3, 0x27, - 0x04, 0xa4, 0xd6, 0x7e, 0xd2, 0xff, 0xbc, 0xcd, 0xbd, 0xbc, 0xad, 0x76, 0xef, 0x4b, 0x25, 0xcb, - 0x8f, 0x34, 0xda, 0x87, 0x0d, 0x0d, 0x65, 0x65, 0x05, 0x8a, 0xed, 0x2f, 0xda, 0xcd, 0x23, 0x12, - 0x96, 0x1b, 0x00, 0xad, 0x76, 0xa3, 0xdb, 0xed, 0x37, 0x51, 0x78, 0x16, 0x18, 0x83, 0x8d, 0xa6, - 0xd6, 0x6e, 0x0c, 0xdb, 0x7a, 0xa3, 0xd9, 0xec, 0x1f, 0xf5, 0x86, 0x4a, 0x11, 0x4b, 0x6c, 0x74, - 0x87, 0x6d, 0x2d, 0x06, 0xd1, 0xfb, 0x6d, 0x2d, 0xad, 0x7f, 0x18, 0x43, 0xca, 0xbb, 0x65, 0xb4, - 0x23, 0x69, 0xac, 0xd4, 0x3f, 0xde, 0x82, 0x8d, 0xf4, 0xd4, 0x64, 0x1f, 0x43, 0xc9, 0x34, 0x53, - 0x63, 0x7c, 0x63, 0xd5, 0x14, 0x7e, 0xd0, 0x32, 0xa3, 0x61, 0xe6, 0x3f, 0xd8, 0x6b, 0xd1, 0x42, - 0x5a, 0x5f, 0x5a, 0x48, 0xd1, 0x32, 0xfa, 0x01, 0x6c, 0x8a, 0x07, 0x6c, 0x4c, 0x23, 0x34, 0x8e, - 0x8d, 0xc0, 0x4a, 0xaf, 0x92, 0x26, 0x21, 0x5b, 0x02, 0xb7, 0xbf, 0xa6, 0x6d, 0x8c, 0x52, 0x10, - 0xf6, 0x7d, 0xd8, 0x30, 0xb8, 0x07, 0x29, 0xe2, 0xcf, 0xc9, 0xaa, 0x6c, 0x83, 0x1c, 0x49, 0x09, - 0x7b, 0xcd, 0x90, 0x01, 0x38, 0x11, 0x4d, 0xdf, 0x9b, 0x25, 0xcc, 0xf9, 0xd4, 0x29, 0xae, 0xef, - 0xcd, 0x24, 0xde, 0xaa, 0x29, 0xa5, 0xd9, 0x47, 0x50, 0x15, 0x35, 0x4f, 0xdc, 0x25, 0xf1, 0x92, - 0xe5, 0xd5, 0x26, 0xd5, 0x74, 0x7f, 0x4d, 0xab, 0x8c, 0x92, 0x24, 0x7b, 0x84, 0xfa, 0x68, 0xa2, - 0xc8, 0x17, 0xe5, 0xb9, 0x46, 0xb5, 0x8d, 0xb8, 0xc0, 0x88, 0x53, 0xec, 0x3d, 0x00, 0xaa, 0x27, - 0xe7, 0x29, 0xa5, 0xe2, 0x9b, 0x7c, 0x6f, 0x16, 0xb1, 0x94, 0xcd, 0x28, 0x21, 0x55, 0x8f, 0x3b, - 0xbb, 0xca, 0xcb, 0xd5, 0x23, 0x87, 0x57, 0x52, 0x3d, 0xee, 0x27, 0x8b, 0xab, 0xc7, 0xd9, 0x60, - 0xa9, 0x7a, 0x11, 0x17, 0xaf, 0x1e, 0x67, 0x8a, 0xaa, 0xc7, 0x79, 0x2a, 0x8b, 0xd5, 0x8b, 0x58, - 0xa8, 0x7a, 0x9c, 0xe3, 0xfb, 0x4b, 0x16, 0x48, 0xf5, 0xb9, 0x16, 0x08, 0x0e, 0x5b, 0xda, 0x06, - 0xf9, 0x3e, 0x6c, 0x04, 0x27, 0xde, 0x99, 0x24, 0x40, 0x6a, 0x32, 0xf7, 0xe0, 0xc4, 0x3b, 0x93, - 0x25, 0x48, 0x2d, 0x90, 0x01, 0x58, 0x5b, 0xde, 0x44, 0x3a, 0x71, 0xdd, 0x90, 0x6b, 0x4b, 0x2d, - 0x7c, 0x62, 0x5b, 0x67, 0x58, 0x5b, 0x23, 0x4a, 0x60, 0xa7, 0x24, 0xae, 0xa3, 0x40, 0x38, 0x83, - 0x52, 0x51, 0x41, 0xa2, 0x24, 0x88, 0x9d, 0x48, 0x01, 0xce, 0xad, 0xb9, 0x2b, 0xb3, 0x29, 0xf2, - 0xdc, 0x3a, 0x72, 0x53, 0x8c, 0x55, 0x4e, 0x2a, 0x58, 0x93, 0x55, 0x11, 0x58, 0x5f, 0xcf, 0x2d, - 0x77, 0x64, 0x89, 0xe8, 0xbf, 0xd4, 0xaa, 0x18, 0x08, 0x5c, 0xb2, 0x2a, 0x22, 0x48, 0x3c, 0xaf, - 0x63, 0x76, 0xb6, 0x38, 0xaf, 0x25, 0x66, 0x9a, 0xd7, 0x31, 0x6b, 0xbc, 0xa0, 0x62, 0xde, 0x4b, - 0x4b, 0x0b, 0x4a, 0x62, 0xe6, 0x0b, 0x2a, 0xe6, 0x7e, 0x04, 0x62, 0x36, 0xf1, 0xce, 0x4d, 0xc5, - 0x08, 0xf2, 0x5a, 0x8b, 0xde, 0x85, 0x51, 0x9c, 0xc2, 0xb9, 0xea, 0x5b, 0x68, 0xf1, 0x88, 0xa9, - 0x70, 0x59, 0x9e, 0xab, 0x1a, 0x61, 0xe2, 0xa5, 0xe4, 0x27, 0x49, 0xa9, 0xb0, 0x99, 0x1d, 0xfa, - 0x75, 0x73, 0xb9, 0xb0, 0x43, 0x3b, 0xf4, 0x93, 0xc2, 0x30, 0xc5, 0xde, 0x01, 0x9a, 0x86, 0x9c, - 0xc5, 0x92, 0x45, 0x37, 0x76, 0x8b, 0x60, 0x28, 0x99, 0xe2, 0x37, 0x4e, 0x16, 0x51, 0xc6, 0xc8, - 0x1c, 0xd5, 0xc7, 0xf2, 0x64, 0xe1, 0x45, 0x34, 0x5b, 0x4d, 0x9c, 0x2c, 0x9c, 0xa8, 0x69, 0x8e, - 0xd8, 0x7d, 0x20, 0x6e, 0xa2, 0x9f, 0xa4, 0xde, 0xa2, 0xf3, 0xbd, 0x19, 0xa7, 0x2e, 0x22, 0x01, - 0xd2, 0x62, 0x0b, 0x1c, 0xcf, 0x8d, 0x1a, 0x7e, 0x92, 0x6a, 0x01, 0x22, 0x62, 0x61, 0x30, 0x8a, - 0x53, 0xea, 0xef, 0x15, 0xa0, 0x28, 0x64, 0x2d, 0xbb, 0x04, 0x9b, 0x42, 0xe4, 0xb7, 0x1a, 0xc3, - 0xc6, 0x6e, 0x63, 0x80, 0x4a, 0x1a, 0x83, 0x0d, 0x2e, 0xf3, 0x63, 0x58, 0x06, 0xf7, 0x01, 0x12, - 0xfa, 0x31, 0x68, 0x1d, 0xf7, 0x01, 0xc1, 0xcb, 0xdf, 0xfc, 0xcc, 0xb2, 0x4d, 0xa8, 0x70, 0x46, - 0x0e, 0xa0, 0xcb, 0xc4, 0xc4, 0xc5, 0xd3, 0x79, 0x89, 0x85, 0x1f, 0x7a, 0x16, 0x12, 0x16, 0x0e, - 0x28, 0xc6, 0x2c, 0xd1, 0xa9, 0x28, 0x83, 0x8d, 0xa1, 0x76, 0xd4, 0x6b, 0x26, 0xe5, 0x94, 0xe9, - 0x02, 0x28, 0xcf, 0xe6, 0x49, 0xa7, 0xfd, 0x54, 0x01, 0x64, 0xe2, 0xb9, 0x50, 0xba, 0x82, 0x6a, - 0x26, 0x65, 0x42, 0xc9, 0x2a, 0xbb, 0x0a, 0x97, 0x06, 0xfb, 0xfd, 0xa7, 0x3a, 0x67, 0x8a, 0x9b, - 0x50, 0x63, 0xdb, 0xa0, 0x48, 0x08, 0x9e, 0xfd, 0x06, 0x16, 0x49, 0xd0, 0x88, 0x70, 0xa0, 0x6c, - 0x52, 0x00, 0x01, 0xc2, 0x86, 0x7c, 0xdf, 0x55, 0xb0, 0x29, 0x9c, 0xb5, 0xdf, 0x3d, 0x3a, 0xe8, - 0x0d, 0x94, 0x2d, 0xac, 0x04, 0x41, 0x78, 0xcd, 0x59, 0x9c, 0x4d, 0xb2, 0x5b, 0x5f, 0xa2, 0x0d, - 0x1c, 0x61, 0x4f, 0x1b, 0x5a, 0xaf, 0xd3, 0x7b, 0x3c, 0x50, 0xb6, 0xe3, 0x9c, 0xdb, 0x9a, 0xd6, - 0xd7, 0x06, 0xca, 0xe5, 0x18, 0x30, 0x18, 0x36, 0x86, 0x47, 0x03, 0xe5, 0x4a, 0x5c, 0xcb, 0x43, - 0xad, 0xdf, 0x6c, 0x0f, 0x06, 0xdd, 0xce, 0x60, 0xa8, 0x5c, 0x65, 0x97, 0x61, 0x2b, 0xa9, 0x51, - 0x44, 0x5c, 0x97, 0x2a, 0xaa, 0x3d, 0x6e, 0x0f, 0x95, 0x6b, 0x71, 0x35, 0x9a, 0xfd, 0x6e, 0xb7, - 0x41, 0xa7, 0xe3, 0xd7, 0x91, 0x88, 0x02, 0x02, 0x44, 0x6b, 0x5e, 0xc1, 0x7a, 0x1d, 0xf5, 0x64, - 0xd0, 0x0d, 0x69, 0x6a, 0x0c, 0xda, 0x3f, 0x3a, 0x6a, 0xf7, 0x9a, 0x6d, 0xe5, 0xd5, 0x64, 0x6a, - 0xc4, 0xb0, 0x9b, 0xf1, 0xd4, 0x88, 0x41, 0xb7, 0xe2, 0x32, 0x23, 0xd0, 0x40, 0xd9, 0xc1, 0xfc, - 0x44, 0x3d, 0x7a, 0xbd, 0x76, 0x73, 0x88, 0x6d, 0x7d, 0x2d, 0xee, 0xc5, 0xa3, 0xc3, 0xc7, 0x5a, - 0xa3, 0xd5, 0x56, 0x54, 0x84, 0x68, 0xed, 0x5e, 0xe3, 0x20, 0x1a, 0xed, 0xdb, 0xd2, 0x68, 0x1f, - 0x76, 0x86, 0x9a, 0x72, 0x27, 0x1e, 0x5d, 0x4a, 0xbe, 0xce, 0x5e, 0x81, 0xab, 0xf2, 0x3c, 0xd4, - 0x9f, 0x76, 0x86, 0xfb, 0xe2, 0x30, 0xff, 0x2e, 0x3f, 0x88, 0x26, 0x64, 0xb3, 0xd5, 0xe4, 0x51, - 0x0b, 0xc4, 0x8b, 0xa9, 0x7b, 0xbb, 0x55, 0x7a, 0x48, 0x5e, 0x28, 0x20, 0xea, 0x67, 0xc0, 0xe4, - 0x77, 0x92, 0x45, 0x10, 0x35, 0x83, 0xdc, 0xd8, 0xf7, 0xa6, 0xd1, 0xc3, 0x1e, 0xf8, 0x1b, 0x6d, - 0xf8, 0xd9, 0xfc, 0x98, 0x0e, 0xc8, 0x93, 0x8b, 0xfb, 0x32, 0x48, 0xfd, 0x9b, 0x19, 0xd8, 0x48, - 0x2b, 0x1f, 0xe4, 0xb4, 0x1d, 0xeb, 0xae, 0x17, 0xf2, 0xc7, 0xd7, 0x82, 0xf8, 0xf9, 0xe2, 0x71, - 0xcf, 0x0b, 0xe9, 0xf5, 0x35, 0x72, 0x29, 0xc4, 0xba, 0x04, 0xcf, 0x35, 0x4e, 0xb3, 0x0e, 0x5c, - 0x4a, 0x3d, 0x4b, 0x9d, 0x7a, 0xfa, 0xae, 0x1e, 0x3f, 0xe0, 0xba, 0x50, 0x7f, 0x8d, 0x05, 0xcb, - 0x6d, 0x12, 0xcf, 0x2f, 0xe4, 0x92, 0xe7, 0x17, 0xf6, 0xa1, 0x96, 0xd2, 0x75, 0xc8, 0x13, 0x34, - 0x4e, 0xd7, 0xb4, 0x64, 0x8f, 0x5f, 0x5e, 0x4d, 0xf5, 0xaf, 0x67, 0xa0, 0x2a, 0x6b, 0x3e, 0xdf, - 0x39, 0x27, 0x8a, 0x6d, 0x12, 0xbf, 0x75, 0xdb, 0x8c, 0x5e, 0x52, 0x8b, 0x40, 0x1d, 0xfa, 0x80, - 0x06, 0xf7, 0x7f, 0xef, 0x9d, 0x0e, 0xe2, 0xe6, 0xc8, 0x20, 0x76, 0x13, 0x80, 0x5e, 0x39, 0xda, - 0xfb, 0x1c, 0x09, 0xc4, 0xfb, 0xe1, 0x09, 0x44, 0xbd, 0x05, 0xe5, 0xbd, 0xd3, 0x28, 0xd4, 0x4b, - 0x7e, 0x82, 0xb0, 0xcc, 0x5f, 0x7b, 0x50, 0xff, 0x28, 0x03, 0x1b, 0x89, 0xdf, 0x83, 0x42, 0x13, - 0xf8, 0x73, 0xe6, 0x7c, 0x3a, 0xac, 0x9b, 0xc7, 0xc9, 0x19, 0xea, 0xba, 0x7c, 0x86, 0x7a, 0x5b, - 0x64, 0x96, 0x95, 0x45, 0x7e, 0x5c, 0x96, 0x78, 0x4b, 0xe2, 0x11, 0x54, 0xf1, 0xbf, 0x66, 0x8d, - 0x2d, 0xdf, 0xb7, 0xcc, 0xf4, 0xd5, 0x96, 0x84, 0x38, 0x45, 0x44, 0x36, 0x9e, 0x38, 0x9d, 0x7d, - 0xce, 0x33, 0x51, 0xf4, 0x7c, 0xd9, 0x7f, 0xc9, 0x42, 0x45, 0xd2, 0x23, 0xbf, 0xd1, 0xf4, 0xbb, - 0x01, 0xe5, 0xe4, 0xd1, 0x22, 0x71, 0x81, 0x3f, 0x06, 0xa4, 0xc6, 0x2a, 0xbb, 0x30, 0x56, 0x75, - 0x28, 0xfa, 0xfc, 0xc2, 0xad, 0x70, 0x43, 0x47, 0xc9, 0xb4, 0xc3, 0x37, 0xff, 0x92, 0x33, 0x9a, - 0xf7, 0xa1, 0x2a, 0x79, 0x6b, 0x83, 0x7a, 0x41, 0x7e, 0xb9, 0x2a, 0xa6, 0xaf, 0x24, 0x9e, 0xdb, - 0x80, 0x5d, 0x86, 0xc2, 0xf8, 0x54, 0x37, 0x8f, 0xf9, 0xb5, 0xee, 0xb2, 0x96, 0x1f, 0x9f, 0xb6, - 0x8e, 0xe9, 0x04, 0x6b, 0x1c, 0xab, 0x4e, 0xdc, 0x87, 0x56, 0x1a, 0x47, 0x0a, 0xd2, 0x3d, 0x28, - 0x8e, 0x4f, 0xe5, 0xeb, 0xd9, 0x4b, 0x5d, 0x5e, 0x18, 0x9f, 0xd2, 0xa5, 0xec, 0x77, 0x61, 0x5b, - 0xec, 0xdf, 0x46, 0xa0, 0xf3, 0xb7, 0x52, 0xe8, 0x31, 0x2b, 0xfe, 0x20, 0xe1, 0x16, 0xc7, 0x35, - 0x82, 0x01, 0x61, 0x70, 0xc6, 0xa9, 0x50, 0x95, 0x26, 0x20, 0xf7, 0x85, 0x95, 0xb5, 0x14, 0x8c, - 0x7d, 0x02, 0xd5, 0xf1, 0x29, 0x1f, 0xd0, 0xa1, 0x77, 0x60, 0x89, 0xdb, 0x26, 0xdb, 0x8b, 0x43, - 0x49, 0xb1, 0x23, 0x29, 0x4a, 0x76, 0x05, 0x0a, 0x9a, 0x71, 0x36, 0xf8, 0x51, 0x97, 0x94, 0xc8, - 0xb2, 0x26, 0x52, 0x9f, 0xe5, 0x4a, 0x1b, 0xca, 0xa6, 0xfa, 0x0f, 0x32, 0xb0, 0x91, 0xd8, 0x00, - 0xb8, 0x08, 0xd9, 0x7d, 0xf9, 0x3b, 0x04, 0xf5, 0x45, 0x33, 0x01, 0x49, 0x1e, 0x0c, 0x2f, 0x66, - 0xfc, 0x7d, 0xdc, 0x55, 0x2f, 0xb5, 0xad, 0x72, 0x9f, 0x67, 0x57, 0xbe, 0x6a, 0xfe, 0x18, 0xb2, - 0xc3, 0x8b, 0x19, 0xf7, 0x37, 0xe1, 0x96, 0xc8, 0x6d, 0x53, 0xbe, 0x19, 0x52, 0xa0, 0xd2, 0xe7, - 0xed, 0x2f, 0xf9, 0x83, 0x23, 0x87, 0x5a, 0xe7, 0xa0, 0xa1, 0x7d, 0xa9, 0x23, 0x80, 0x94, 0x86, - 0xbd, 0xbe, 0xd6, 0xee, 0x3c, 0xee, 0x11, 0x20, 0x47, 0xde, 0xa8, 0xa4, 0x8a, 0x0d, 0xd3, 0xdc, - 0x3b, 0x95, 0x1f, 0xb9, 0xca, 0xa4, 0x1e, 0xb9, 0x8a, 0xdf, 0x83, 0x93, 0xdf, 0xd1, 0x0c, 0x63, - 0x9f, 0x3e, 0x8b, 0x57, 0x61, 0xbc, 0xa4, 0xd9, 0x1b, 0x90, 0x1b, 0x9f, 0x5a, 0x17, 0x69, 0x43, - 0x2f, 0xbd, 0x80, 0x88, 0x40, 0xfd, 0x45, 0x06, 0x58, 0xaa, 0x22, 0xdc, 0xf6, 0xf8, 0xae, 0x75, - 0xf9, 0x18, 0xea, 0x22, 0x9a, 0x93, 0x53, 0x49, 0xfe, 0x7a, 0xd1, 0xa5, 0x97, 0xbd, 0x24, 0xca, - 0x39, 0x79, 0x4c, 0x8e, 0xbd, 0x0b, 0xfc, 0x89, 0x4c, 0x0a, 0x35, 0xca, 0x3d, 0xc7, 0x4e, 0xd4, - 0x12, 0x9a, 0xe4, 0x4d, 0x4c, 0xf9, 0xad, 0x4f, 0xee, 0xea, 0xdf, 0x4c, 0x46, 0x8d, 0xd6, 0xbc, - 0xfa, 0xff, 0x65, 0xe0, 0x52, 0x7a, 0x42, 0xfc, 0x72, 0xad, 0x4c, 0x3f, 0x6c, 0x9a, 0x5d, 0x7c, - 0xd8, 0x74, 0xd5, 0x7c, 0xca, 0xad, 0x9c, 0x4f, 0xbf, 0x9b, 0x81, 0x6d, 0xa9, 0xf7, 0x13, 0x6b, - 0xf1, 0x2f, 0xa8, 0x66, 0xd2, 0xfb, 0xa6, 0xb9, 0xd4, 0xfb, 0xa6, 0xea, 0xcf, 0x33, 0x70, 0x65, - 0xa1, 0x26, 0x9a, 0xf5, 0x17, 0x5a, 0x97, 0xf4, 0x3b, 0xa8, 0x74, 0xdc, 0x10, 0x3d, 0xca, 0x91, - 0xb9, 0x97, 0x95, 0xde, 0x41, 0xa5, 0x33, 0x28, 0x3a, 0x08, 0x7d, 0x55, 0xbc, 0xdd, 0xa4, 0x07, - 0x17, 0xee, 0x48, 0x0c, 0x76, 0x99, 0x20, 0x83, 0x0b, 0x77, 0xa4, 0xfe, 0x71, 0x06, 0xae, 0x2d, - 0xb4, 0xa1, 0x31, 0x0f, 0x3d, 0x71, 0x9e, 0xfc, 0x17, 0xd4, 0x8c, 0x5b, 0x50, 0xa1, 0xd3, 0x76, - 0x71, 0x48, 0xcd, 0xbb, 0x15, 0x8c, 0xa4, 0x5c, 0x05, 0xb2, 0xa6, 0x71, 0x21, 0x1e, 0x3e, 0xc0, - 0x9f, 0xb8, 0x60, 0x4f, 0xbc, 0xb9, 0x2f, 0x22, 0x8c, 0xe8, 0xb7, 0xfa, 0x01, 0x6c, 0x25, 0x55, - 0x6f, 0x8a, 0xe7, 0x68, 0x6f, 0x41, 0xc5, 0xb5, 0xce, 0xf4, 0xe8, 0xb1, 0x5a, 0x11, 0x37, 0xe7, - 0x5a, 0x67, 0x82, 0x40, 0xdd, 0x93, 0x65, 0x61, 0xfc, 0xc9, 0x0f, 0xc7, 0x4c, 0xc5, 0xfe, 0x78, - 0x8e, 0x19, 0xa1, 0x30, 0x37, 0xa9, 0x95, 0x45, 0xd7, 0x3a, 0xa3, 0x79, 0x78, 0x26, 0xf2, 0x69, - 0x98, 0xe2, 0x71, 0xd8, 0x95, 0x4f, 0xbd, 0x5d, 0x83, 0xd2, 0xcc, 0x4f, 0x75, 0x53, 0x71, 0xe6, - 0xf3, 0x62, 0xef, 0x88, 0xa8, 0xbc, 0xe7, 0xc5, 0x4a, 0xf0, 0x38, 0x3d, 0xf1, 0x49, 0xa0, 0x5c, - 0xf2, 0x49, 0xa0, 0x0f, 0x85, 0x18, 0x24, 0xbb, 0x8f, 0x97, 0xac, 0x40, 0xd6, 0x36, 0xcf, 0xa9, - 0xe0, 0x9a, 0x86, 0x3f, 0x49, 0x93, 0xb3, 0xbe, 0x16, 0x81, 0x81, 0xf8, 0x53, 0xdd, 0x85, 0x8a, - 0x96, 0x32, 0x72, 0xab, 0x92, 0xbf, 0x28, 0x48, 0xbf, 0x86, 0x95, 0x74, 0x90, 0x56, 0x49, 0xdc, - 0x45, 0x81, 0x1a, 0x08, 0xc1, 0xf7, 0xc4, 0xf0, 0x47, 0x27, 0x86, 0xdf, 0xb5, 0xdc, 0x49, 0x78, - 0x82, 0x5d, 0xce, 0xdd, 0xb8, 0x72, 0x17, 0x02, 0x07, 0x45, 0xd3, 0x01, 0x7b, 0xd1, 0x21, 0xf2, - 0xe8, 0x03, 0x22, 0xae, 0x75, 0x26, 0xf8, 0x5f, 0x05, 0xc0, 0xfe, 0x17, 0x68, 0x7e, 0x8c, 0x59, - 0xf6, 0x1c, 0x93, 0xa3, 0xd5, 0x2d, 0xd1, 0x5e, 0xf1, 0x70, 0x47, 0xcb, 0x1a, 0xab, 0x8e, 0x18, - 0x79, 0xde, 0x20, 0xd1, 0x09, 0xdf, 0x69, 0x18, 0xd9, 0x6b, 0x50, 0x8d, 0x3c, 0x12, 0xf4, 0x00, - 0x1b, 0x2f, 0xbe, 0x12, 0xc1, 0x7a, 0xf3, 0xa9, 0xfa, 0x07, 0x59, 0xa8, 0x36, 0x78, 0x74, 0xd0, - 0xec, 0xa2, 0x3f, 0x0b, 0xd9, 0x6f, 0xc1, 0x65, 0x7a, 0x91, 0x85, 0x3f, 0xb3, 0x4c, 0x41, 0x39, - 0x14, 0xe2, 0x2f, 0x3a, 0xf1, 0xbe, 0xd4, 0x89, 0x82, 0xe5, 0xc1, 0xe0, 0xd4, 0x9e, 0xf1, 0x9b, - 0x25, 0x1d, 0xf3, 0x9c, 0xae, 0x71, 0xf0, 0xf8, 0x02, 0x7a, 0xbc, 0x25, 0x8d, 0xa0, 0xf7, 0x1b, - 0x30, 0xfb, 0xd9, 0xa9, 0xc8, 0x56, 0x84, 0x5e, 0x20, 0xf0, 0xf0, 0x94, 0xd3, 0xdc, 0x87, 0x2d, - 0x7e, 0x99, 0x6c, 0x79, 0x03, 0xde, 0xe4, 0x88, 0x64, 0x7e, 0x0f, 0x60, 0x4b, 0x3c, 0x20, 0x43, - 0x2f, 0x8a, 0xea, 0x23, 0x6f, 0x76, 0x21, 0x8e, 0x2f, 0xdf, 0x78, 0x4e, 0x55, 0x3b, 0x9c, 0x14, - 0x41, 0xbc, 0x9e, 0x9b, 0x41, 0x1a, 0x7a, 0xbd, 0x0d, 0x57, 0x9f, 0xd3, 0xa6, 0x97, 0x85, 0x48, - 0x94, 0xa4, 0x10, 0x89, 0xeb, 0xbb, 0xb0, 0xbd, 0xaa, 0xbc, 0x6f, 0x93, 0x87, 0xfa, 0xb3, 0x1a, - 0x40, 0x32, 0x63, 0x53, 0xea, 0x68, 0x66, 0x41, 0x1d, 0xfd, 0x56, 0x81, 0x41, 0x1f, 0xc0, 0x06, - 0x76, 0x95, 0x9e, 0x70, 0x64, 0x57, 0x72, 0x54, 0x91, 0x6a, 0x98, 0xdc, 0x97, 0x5d, 0x0e, 0xab, - 0xc8, 0xad, 0x0c, 0xab, 0x78, 0x1f, 0x8a, 0xfc, 0xa0, 0x2d, 0x10, 0x57, 0xb4, 0xaf, 0x2e, 0xae, - 0xbe, 0x07, 0xe2, 0x96, 0x4a, 0x44, 0xc7, 0xda, 0xb0, 0x81, 0xa2, 0xdf, 0xb7, 0xc3, 0x93, 0xa9, - 0x7c, 0x61, 0xfb, 0xe6, 0x32, 0x67, 0x44, 0x26, 0xbe, 0xda, 0x22, 0x27, 0x25, 0xed, 0x35, 0x9c, - 0x0a, 0xef, 0x2f, 0x69, 0xaf, 0x45, 0x59, 0x7b, 0x1d, 0x4e, 0xb9, 0xcf, 0x17, 0xb5, 0xd7, 0x77, - 0xe0, 0x92, 0xb8, 0x39, 0x87, 0x0c, 0xd8, 0x9d, 0x44, 0xcf, 0x03, 0x35, 0x15, 0xf1, 0x6e, 0xcb, - 0x94, 0x6c, 0x3b, 0x24, 0xff, 0x02, 0xb6, 0x47, 0x27, 0x86, 0x3b, 0xb1, 0xf4, 0xf0, 0xd8, 0xd1, - 0xe9, 0xdb, 0x0e, 0xfa, 0xd4, 0x98, 0x09, 0xa5, 0xfa, 0x8d, 0xa5, 0xca, 0x36, 0x89, 0x78, 0x78, - 0xec, 0x50, 0x10, 0x5b, 0x1c, 0x7c, 0xb3, 0x35, 0x5a, 0x84, 0x2f, 0x9c, 0x81, 0xc3, 0xd2, 0x19, - 0xf8, 0xa2, 0x9a, 0x5d, 0x59, 0xa1, 0x66, 0x27, 0xca, 0x72, 0x55, 0x56, 0x96, 0xd9, 0xdb, 0x50, - 0x14, 0x17, 0x7f, 0x85, 0xdf, 0x97, 0x2d, 0xaf, 0x0e, 0x2d, 0x22, 0xc1, 0x92, 0xa2, 0x88, 0x0c, - 0x7a, 0x1b, 0x62, 0x83, 0x97, 0x24, 0xc3, 0xd8, 0xae, 0x70, 0x7a, 0xc6, 0x01, 0x77, 0xc2, 0xc7, - 0x7b, 0x5d, 0xca, 0x38, 0xc6, 0x09, 0xbb, 0x7c, 0x81, 0xe3, 0xfa, 0xdf, 0x2f, 0x40, 0x41, 0x9c, - 0xab, 0xdf, 0x87, 0x9c, 0xe9, 0x7b, 0xb3, 0x38, 0x62, 0x7d, 0x85, 0xd6, 0x4e, 0x1f, 0x17, 0x44, - 0x05, 0xff, 0x01, 0x14, 0x0c, 0xd3, 0xd4, 0xc7, 0xa7, 0xe9, 0xf3, 0xe8, 0x05, 0x05, 0x7a, 0x7f, - 0x4d, 0xcb, 0x1b, 0xa4, 0x49, 0x7f, 0x0c, 0x65, 0xa4, 0x4f, 0x42, 0x58, 0x2b, 0xcb, 0x66, 0x41, - 0xa4, 0xea, 0xee, 0xaf, 0x69, 0x25, 0x23, 0x52, 0x7b, 0x7f, 0x3d, 0xed, 0xd9, 0xcf, 0x2d, 0x35, - 0x70, 0x41, 0x4f, 0x5b, 0xf0, 0xf1, 0xff, 0x06, 0x70, 0x57, 0x6f, 0xbc, 0x63, 0xe7, 0xe5, 0xa3, - 0xcf, 0xa5, 0xfd, 0x7d, 0x7f, 0x4d, 0xe3, 0xfb, 0x56, 0xb4, 0xdf, 0x7f, 0x18, 0x79, 0xdd, 0xe3, - 0x0f, 0x2b, 0xad, 0xe8, 0x19, 0x14, 0x83, 0xb1, 0xeb, 0x9d, 0x64, 0x22, 0xb2, 0x99, 0xd1, 0x6b, - 0xef, 0xe2, 0xb4, 0x44, 0x66, 0x8b, 0x77, 0x75, 0x62, 0x8b, 0xb7, 0xf8, 0x4f, 0xa0, 0xc2, 0x9d, - 0xb0, 0x9c, 0xaf, 0xb4, 0xd4, 0xb5, 0xc9, 0xa6, 0x4c, 0xc7, 0x7a, 0xc9, 0x16, 0xdd, 0x8c, 0xda, - 0xe9, 0x5b, 0xf2, 0xc9, 0xc9, 0x8d, 0x95, 0x1d, 0xa5, 0xc5, 0x87, 0x28, 0xbc, 0xb1, 0x1a, 0xe7, - 0x61, 0x5d, 0xd8, 0x16, 0x47, 0x0c, 0x7c, 0x03, 0x8e, 0xf6, 0x4c, 0x58, 0x1a, 0xaf, 0xd4, 0x0e, - 0xbd, 0xbf, 0xa6, 0x31, 0x63, 0x79, 0xdf, 0x6e, 0xc2, 0x56, 0x54, 0x25, 0xda, 0x59, 0xa5, 0xd0, - 0x2b, 0xb9, 0x49, 0xc9, 0xbe, 0xbb, 0xbf, 0xa6, 0x6d, 0x1a, 0x69, 0x10, 0xeb, 0xc0, 0xa5, 0x28, - 0x13, 0x72, 0xb5, 0x8b, 0x9e, 0xa9, 0x2e, 0x8d, 0xa2, 0xbc, 0x57, 0xef, 0xaf, 0x69, 0x5b, 0xc6, - 0xd2, 0x06, 0x7e, 0x10, 0xd5, 0x47, 0x56, 0x0e, 0xf9, 0x4a, 0xbc, 0xb5, 0xb2, 0x9b, 0x12, 0x4d, - 0x35, 0xae, 0x59, 0x02, 0x4a, 0xe2, 0x18, 0xae, 0x6b, 0x70, 0x65, 0xb5, 0x84, 0x91, 0xb7, 0x99, - 0x1c, 0xdf, 0x66, 0x54, 0x79, 0x9b, 0x59, 0x7c, 0xeb, 0x45, 0xda, 0x74, 0x7e, 0x08, 0xb5, 0x94, - 0x88, 0x65, 0x15, 0x28, 0x46, 0x2f, 0xdd, 0xd3, 0x05, 0x9b, 0x66, 0xff, 0xf0, 0x4b, 0x25, 0x83, - 0xe0, 0x4e, 0x6f, 0x30, 0x6c, 0xf4, 0x86, 0xca, 0x3a, 0x4f, 0x1c, 0x76, 0x1b, 0xcd, 0xb6, 0x92, - 0x55, 0x7f, 0x9e, 0x85, 0x72, 0x7c, 0xca, 0xf6, 0xdd, 0xbd, 0x61, 0xb1, 0x9b, 0x29, 0x2b, 0xbb, - 0x99, 0x16, 0x4c, 0x3d, 0xfe, 0xfd, 0x8a, 0x5c, 0xf4, 0x4d, 0x03, 0xd9, 0xa0, 0x0a, 0x96, 0x9f, - 0x00, 0xc8, 0x7f, 0xc3, 0x27, 0x00, 0xe4, 0x28, 0xf6, 0x42, 0x3a, 0x8a, 0x7d, 0xe1, 0x6b, 0x07, - 0x45, 0x7a, 0x87, 0x5c, 0xfe, 0xda, 0x01, 0x7d, 0xf6, 0xf5, 0x89, 0x6d, 0x9d, 0x89, 0xb0, 0x6f, - 0x91, 0x4a, 0xef, 0xd0, 0xf0, 0x92, 0x1d, 0xfa, 0x9b, 0x48, 0xfb, 0x87, 0xb0, 0x3d, 0x3e, 0x8d, - 0x5f, 0x3f, 0x4f, 0x9c, 0x2b, 0x55, 0xaa, 0xd2, 0x4a, 0x1c, 0x7b, 0x23, 0xfe, 0xf6, 0x5c, 0x4d, - 0x76, 0x03, 0xc5, 0xa3, 0x15, 0x7f, 0x8c, 0xee, 0xff, 0xc9, 0x00, 0x24, 0xe7, 0x4f, 0xbf, 0xb4, - 0x2b, 0x57, 0xf2, 0x96, 0x65, 0x5f, 0xe0, 0x2d, 0x7b, 0xd9, 0x2b, 0x77, 0x5f, 0x43, 0x39, 0x3e, - 0x71, 0xfc, 0xee, 0x13, 0xeb, 0x5b, 0x15, 0xf9, 0xdb, 0x91, 0x5b, 0x3b, 0x3e, 0xb2, 0xfb, 0x65, - 0xfb, 0x22, 0x55, 0x7c, 0xf6, 0x25, 0xc5, 0x9f, 0x73, 0xdf, 0x72, 0x5c, 0xf8, 0xaf, 0x78, 0x35, - 0xc9, 0x13, 0x3d, 0x97, 0x9a, 0xe8, 0xea, 0x5c, 0x38, 0xc8, 0x7f, 0xf9, 0xa2, 0xbf, 0x55, 0x83, - 0xff, 0x73, 0x26, 0xf2, 0xe2, 0xc6, 0x0f, 0xd6, 0x3f, 0x57, 0xe9, 0x5d, 0xed, 0x88, 0xfe, 0x36, - 0xc5, 0xbd, 0xd0, 0x47, 0x95, 0x7b, 0x91, 0x8f, 0xea, 0x0d, 0xc8, 0xf3, 0xed, 0x2e, 0xff, 0x3c, - 0xff, 0x14, 0xc7, 0xbf, 0xf4, 0x0b, 0x34, 0xaa, 0x2a, 0x94, 0x7c, 0xde, 0xde, 0xed, 0x28, 0xdf, - 0xe8, 0xeb, 0x39, 0x74, 0xcb, 0xe6, 0xff, 0xe0, 0x12, 0xf5, 0xbb, 0x76, 0xc9, 0x8b, 0xdd, 0x16, - 0xea, 0xff, 0xcc, 0x40, 0x2d, 0x15, 0x41, 0xf0, 0x1d, 0x8a, 0x58, 0x29, 0x97, 0xb3, 0xff, 0x1b, - 0xc9, 0xe5, 0x54, 0x18, 0x70, 0x29, 0x1d, 0x06, 0x8c, 0xe2, 0xae, 0x9a, 0x32, 0x61, 0x56, 0x19, - 0x3b, 0x99, 0x95, 0xc6, 0xce, 0xcd, 0xf8, 0x23, 0xa2, 0x9d, 0x16, 0x8f, 0xba, 0xad, 0x69, 0x12, - 0x84, 0x7d, 0x0a, 0xd7, 0x84, 0x13, 0x81, 0xf7, 0x8f, 0x37, 0xd6, 0xe3, 0x4f, 0x8c, 0x0a, 0xa3, - 0xfc, 0x0a, 0x27, 0xe0, 0xdf, 0x0f, 0x1a, 0x37, 0x22, 0xac, 0xda, 0x81, 0x5a, 0x2a, 0x34, 0x43, - 0xfa, 0xfc, 0x71, 0x46, 0xfe, 0xfc, 0x31, 0xdb, 0x81, 0xfc, 0xd9, 0x89, 0xe5, 0x5b, 0x2b, 0x1e, - 0x99, 0xe6, 0x08, 0xf5, 0xfb, 0x50, 0x95, 0xc3, 0xc4, 0xd8, 0xdb, 0x90, 0xb7, 0x43, 0x6b, 0x1a, - 0xb9, 0x47, 0xae, 0x2c, 0x47, 0x92, 0x75, 0x42, 0x6b, 0xaa, 0x71, 0x22, 0xf5, 0x0f, 0x33, 0xa0, - 0x2c, 0xe2, 0xa4, 0x6f, 0x34, 0x67, 0x9e, 0xf3, 0x8d, 0xe6, 0xf5, 0x54, 0x25, 0x57, 0x7d, 0x66, - 0x79, 0x27, 0x52, 0x4a, 0x56, 0x7c, 0xe2, 0x97, 0x10, 0xec, 0x2e, 0x94, 0x7c, 0x8b, 0x3e, 0x80, - 0x6b, 0xae, 0xb8, 0x7d, 0x12, 0xe3, 0xd4, 0xdf, 0xcf, 0x40, 0x51, 0xc4, 0xb4, 0xad, 0xf4, 0x57, - 0xbd, 0x09, 0x45, 0xfe, 0x31, 0xdc, 0xe8, 0xb1, 0xbb, 0xa5, 0x68, 0xdd, 0x08, 0xcf, 0x6e, 0xf2, - 0x48, 0xbf, 0xb4, 0xff, 0xea, 0xd0, 0x31, 0x5c, 0x8d, 0xe0, 0xe2, 0xb3, 0x60, 0xc6, 0x54, 0xbc, - 0x8e, 0xc0, 0x5f, 0x29, 0x03, 0x02, 0xd1, 0x43, 0x08, 0xea, 0xaf, 0x43, 0x51, 0xc4, 0xcc, 0xad, - 0xac, 0xca, 0x4b, 0x3e, 0x6e, 0xaa, 0xee, 0x00, 0x24, 0x41, 0x74, 0xab, 0x72, 0x50, 0xef, 0x43, - 0x29, 0x8a, 0x9b, 0xc3, 0xf9, 0x97, 0x14, 0x2d, 0xae, 0x35, 0xc9, 0x95, 0x71, 0xc4, 0x87, 0x1b, - 0xba, 0xde, 0xe8, 0x94, 0x9c, 0xe5, 0xef, 0x02, 0xdd, 0xf1, 0x1a, 0x2e, 0x3d, 0xe7, 0x96, 0xfe, - 0xc2, 0x47, 0x4c, 0xc4, 0xee, 0x43, 0x2c, 0x2f, 0x5f, 0xe6, 0x5a, 0x50, 0x1b, 0xd1, 0x6d, 0x40, - 0x9a, 0x65, 0x8f, 0x84, 0x37, 0xb5, 0x4b, 0xcf, 0x89, 0x66, 0xe4, 0x07, 0x96, 0x53, 0x75, 0xd2, - 0x24, 0x32, 0x75, 0x03, 0xaa, 0x72, 0xb0, 0x8f, 0xda, 0x80, 0xad, 0x03, 0x2b, 0x34, 0x50, 0xfe, - 0x44, 0x8f, 0x5c, 0xf1, 0xf9, 0x8b, 0x3f, 0xd2, 0xf3, 0x77, 0x91, 0x4e, 0xe3, 0x44, 0xea, 0xcf, - 0x73, 0xa0, 0x2c, 0xe2, 0x5e, 0x74, 0x33, 0xf2, 0x16, 0x54, 0x3c, 0x9a, 0x17, 0xa9, 0x0f, 0xbf, - 0x71, 0x90, 0x74, 0xa7, 0x20, 0xf5, 0x49, 0x9f, 0x92, 0x1d, 0xec, 0xf3, 0x8f, 0xfa, 0x5c, 0xe5, - 0xd7, 0xe0, 0x1c, 0x6f, 0x44, 0xd3, 0xba, 0x4a, 0xb7, 0xde, 0xba, 0xde, 0x88, 0x2e, 0x5c, 0x0a, - 0xef, 0x04, 0x8f, 0x40, 0xad, 0x6a, 0x25, 0xe1, 0x92, 0xa0, 0xf3, 0x3b, 0x71, 0xd3, 0x20, 0x0c, - 0xc4, 0x3d, 0xe2, 0x12, 0x07, 0x0c, 0x83, 0xe8, 0xf3, 0x00, 0x23, 0xf1, 0x95, 0xb2, 0x2c, 0x7d, - 0x1e, 0xa0, 0xe9, 0xd2, 0x7d, 0x4b, 0x7a, 0x34, 0x79, 0x24, 0xbe, 0xcf, 0x28, 0x3e, 0xd0, 0x80, - 0xa8, 0xdb, 0xfc, 0x3b, 0x6e, 0xbe, 0x15, 0x04, 0xfc, 0x55, 0xc9, 0xb2, 0x78, 0x4e, 0x54, 0x00, - 0xe3, 0x47, 0x7b, 0xc5, 0x27, 0xf6, 0x90, 0x04, 0xc4, 0xdb, 0x96, 0xfc, 0x03, 0x7b, 0x48, 0x70, - 0x0d, 0x4a, 0x3f, 0xf1, 0x5c, 0x8b, 0xbc, 0x1c, 0x15, 0xaa, 0x55, 0x11, 0xd3, 0x07, 0xc6, 0x0c, - 0x37, 0x02, 0x87, 0xae, 0xac, 0xf3, 0x9b, 0x86, 0x3c, 0xa1, 0xfe, 0xcb, 0x0c, 0x6c, 0x2f, 0xf6, - 0x35, 0x4d, 0xa3, 0x2a, 0x94, 0x9a, 0xfd, 0xae, 0xde, 0x6b, 0x1c, 0xb4, 0x95, 0x35, 0xb6, 0x09, - 0x95, 0xfe, 0xee, 0x67, 0xed, 0xe6, 0x90, 0x03, 0x32, 0xf4, 0x0e, 0xc4, 0x40, 0xdf, 0xef, 0xb4, - 0x5a, 0xed, 0x1e, 0xb7, 0x28, 0xfa, 0xbb, 0x9f, 0xe9, 0xdd, 0x7e, 0x93, 0x7f, 0x5f, 0x2b, 0x8a, - 0x78, 0x18, 0x28, 0x39, 0x8a, 0x87, 0xa0, 0x60, 0x78, 0x4c, 0xe6, 0x79, 0x94, 0xf7, 0xd3, 0x81, - 0xde, 0xec, 0x0d, 0x95, 0x02, 0xa6, 0x7a, 0x47, 0xdd, 0x2e, 0xa5, 0x28, 0x9c, 0xb3, 0xd9, 0x3f, - 0x38, 0xd4, 0xda, 0x83, 0x81, 0x3e, 0xe8, 0xfc, 0xb8, 0xad, 0x94, 0xa8, 0x64, 0xad, 0xf3, 0xb8, - 0xd3, 0xe3, 0x80, 0x32, 0x2b, 0x42, 0xf6, 0xa0, 0xd3, 0xe3, 0xef, 0x5f, 0x1c, 0x34, 0xbe, 0x50, - 0x2a, 0xf8, 0x63, 0x70, 0x74, 0xa0, 0x54, 0x59, 0x19, 0xf2, 0xdd, 0xf6, 0x93, 0x76, 0x57, 0xa9, - 0xa9, 0xff, 0x36, 0x1b, 0x69, 0xc4, 0x14, 0xe7, 0xf4, 0x4d, 0xb4, 0xc0, 0x55, 0xe7, 0x8b, 0x71, - 0xa7, 0x65, 0xa5, 0x4e, 0xfb, 0x26, 0x5f, 0x94, 0xbe, 0x0d, 0xb5, 0x38, 0x38, 0x40, 0x7a, 0xd2, - 0xbf, 0x1a, 0x01, 0x57, 0x1c, 0x5f, 0x14, 0x56, 0x1c, 0x5f, 0xcc, 0xec, 0x10, 0xad, 0x6c, 0x94, - 0xb9, 0x7c, 0x26, 0x95, 0x11, 0xc2, 0xbf, 0xfb, 0xfe, 0x0a, 0x50, 0x42, 0x9f, 0xbb, 0x76, 0xf4, - 0xb5, 0xcf, 0x12, 0x02, 0x8e, 0x5c, 0x3b, 0x5c, 0x0c, 0x4e, 0x28, 0x2f, 0x05, 0x27, 0xc8, 0x9b, - 0x33, 0xa4, 0x37, 0xe7, 0xf4, 0x87, 0xb6, 0xf9, 0x67, 0x3e, 0xa5, 0x0f, 0x6d, 0xbf, 0x0d, 0x6c, - 0x34, 0xf7, 0xe9, 0x81, 0x3d, 0x89, 0xac, 0x4a, 0x64, 0x8a, 0xc0, 0xc4, 0xbb, 0x22, 0x7b, 0x03, - 0x36, 0x17, 0xa8, 0xc9, 0x96, 0x2e, 0x6b, 0x1b, 0x69, 0x52, 0xf6, 0x00, 0x2e, 0x89, 0xb9, 0x9d, - 0xea, 0x5b, 0x71, 0x7d, 0x95, 0xa3, 0x1a, 0x49, 0x0f, 0xab, 0xbf, 0x06, 0xa5, 0x28, 0xa4, 0xed, - 0xc5, 0xca, 0xee, 0x8a, 0x71, 0x55, 0xff, 0xce, 0x3a, 0x94, 0xe3, 0x00, 0xb7, 0x6f, 0x34, 0x3b, - 0xe8, 0xdb, 0x25, 0xc1, 0xa9, 0x2c, 0x62, 0x4a, 0x08, 0x88, 0x46, 0x4a, 0xdc, 0xf8, 0x9a, 0xfb, - 0x76, 0xa4, 0xb1, 0x71, 0xc8, 0x91, 0x6f, 0xd3, 0x2b, 0x3a, 0xb6, 0x2b, 0x5d, 0x34, 0x2d, 0x6b, - 0x25, 0x04, 0xd0, 0x42, 0xbb, 0x06, 0xf4, 0x9b, 0x38, 0xa3, 0x6f, 0x8f, 0xdb, 0xee, 0x29, 0xf2, - 0x3d, 0xe7, 0xdb, 0xe3, 0xf4, 0xe5, 0x15, 0x1e, 0x5d, 0xc3, 0x63, 0x0a, 0xa2, 0xcf, 0x17, 0xbe, - 0x02, 0xe5, 0x79, 0xfc, 0xfd, 0x4b, 0x31, 0x23, 0xe6, 0xd1, 0xd7, 0x2f, 0xd3, 0xa3, 0x5a, 0x5e, - 0x1c, 0xd5, 0xc5, 0x39, 0x0d, 0x4b, 0x73, 0x5a, 0x0d, 0xa1, 0x28, 0x82, 0xfc, 0x5e, 0xdc, 0xe1, - 0x2f, 0xec, 0x2a, 0x05, 0xb2, 0x86, 0x13, 0xdd, 0x6e, 0xc5, 0x9f, 0x0b, 0x15, 0xcb, 0x2d, 0x54, - 0x4c, 0xfd, 0x6b, 0xeb, 0x00, 0x49, 0xb0, 0x20, 0x7b, 0x67, 0x21, 0x30, 0x39, 0xb3, 0xb4, 0xed, - 0x2f, 0xc4, 0x23, 0x2f, 0x3c, 0x2b, 0xb5, 0xfe, 0x0d, 0x9e, 0x95, 0x7a, 0x08, 0xb5, 0xc0, 0x1f, - 0xbd, 0xd4, 0xe1, 0x5e, 0x09, 0xfc, 0x51, 0xec, 0x6f, 0x7f, 0x17, 0x30, 0x49, 0x4f, 0x4e, 0x26, - 0x86, 0xea, 0x92, 0xd6, 0x52, 0x0e, 0xfc, 0x51, 0xff, 0xf8, 0xab, 0x16, 0xbf, 0x66, 0x67, 0x06, - 0xa1, 0xbe, 0x4a, 0x4a, 0x6c, 0x9a, 0x41, 0xd8, 0x92, 0x05, 0xc5, 0x1d, 0xd8, 0x40, 0xda, 0x25, - 0x61, 0x51, 0x35, 0x83, 0xe4, 0x80, 0x45, 0xfd, 0xbd, 0xe8, 0x48, 0x7a, 0xc1, 0x97, 0xcb, 0x3e, - 0x12, 0x86, 0xb8, 0xa4, 0x44, 0xd4, 0x57, 0xb9, 0x7e, 0xf9, 0x23, 0x58, 0x31, 0xe9, 0xf2, 0xa7, - 0x0c, 0xd7, 0xbf, 0xe9, 0xa7, 0x0c, 0x77, 0x00, 0x92, 0x47, 0x3f, 0x71, 0x05, 0xc6, 0x97, 0x75, - 0xca, 0xfc, 0x1a, 0xce, 0xfd, 0x03, 0xb8, 0xb4, 0xe2, 0xbb, 0xee, 0xec, 0x12, 0x6c, 0x36, 0x1e, - 0x3f, 0xd6, 0x9b, 0xfd, 0xde, 0x5e, 0xe7, 0xb1, 0x2e, 0xae, 0xe5, 0xbc, 0x06, 0xaf, 0x4a, 0xc0, - 0xc7, 0x5a, 0xff, 0xe8, 0x10, 0x13, 0xcd, 0xc6, 0x50, 0x7c, 0x59, 0x3d, 0x73, 0xff, 0x35, 0xa8, - 0xca, 0x5f, 0x5d, 0xa6, 0x3b, 0x3d, 0x9e, 0x6b, 0xf1, 0xaf, 0xca, 0x74, 0x7f, 0xf2, 0x81, 0x92, - 0xb9, 0xaf, 0x42, 0x45, 0xfa, 0xa6, 0x13, 0x52, 0xec, 0x1b, 0xc1, 0x89, 0xf8, 0xc2, 0x88, 0xe1, - 0x4e, 0x2c, 0x25, 0x73, 0xff, 0x2e, 0xea, 0xf0, 0xf2, 0x17, 0x95, 0x00, 0x0a, 0x3d, 0xcf, 0x9f, - 0x1a, 0x8e, 0xa0, 0xb3, 0xe6, 0x01, 0xd2, 0xbd, 0x0b, 0x97, 0x57, 0x7e, 0x1f, 0x8a, 0x6e, 0x84, - 0xd9, 0xd3, 0x99, 0x63, 0xf1, 0xbb, 0x4d, 0xfb, 0x17, 0xc7, 0xbe, 0x6d, 0x2a, 0x99, 0xfb, 0x9f, - 0x2c, 0x7c, 0x3f, 0xe4, 0xa8, 0xb7, 0xdb, 0x3f, 0xea, 0xb5, 0xda, 0x2d, 0x7e, 0xeb, 0xa8, 0xd3, - 0x6b, 0x76, 0x8f, 0x06, 0x9d, 0x27, 0x62, 0x6b, 0x6d, 0x7f, 0x11, 0x25, 0xd7, 0xef, 0xef, 0x47, - 0x0f, 0x44, 0x44, 0xb5, 0xee, 0xf6, 0x1b, 0x2d, 0xbe, 0x25, 0xc7, 0x4f, 0x44, 0x0d, 0x77, 0xf9, - 0x77, 0x47, 0xb4, 0xf6, 0xe0, 0xa8, 0x3b, 0x8c, 0x9e, 0xa3, 0xda, 0x00, 0xe8, 0x34, 0xdb, 0xbb, - 0x6d, 0xed, 0x31, 0x12, 0x64, 0xef, 0xff, 0x10, 0xea, 0xcf, 0xbb, 0x2e, 0x84, 0x6d, 0x6b, 0xee, - 0x37, 0xe8, 0x4a, 0x16, 0x6e, 0xc9, 0x7d, 0x9d, 0xa7, 0xc8, 0x71, 0xa8, 0xb5, 0xbb, 0x6d, 0x8a, - 0xa9, 0xbd, 0xff, 0xd3, 0x8c, 0xa4, 0x9e, 0x46, 0x57, 0x3e, 0x62, 0x80, 0xe8, 0x70, 0x19, 0xa4, - 0x59, 0x86, 0xa9, 0x64, 0xd8, 0x15, 0x60, 0x29, 0x50, 0xd7, 0x1b, 0x19, 0x8e, 0xb2, 0x4e, 0xd1, - 0xb3, 0x11, 0x9c, 0xae, 0x17, 0x2a, 0x59, 0xf6, 0x2a, 0x5c, 0x8b, 0x61, 0x5d, 0xef, 0xec, 0xd0, - 0xb7, 0x3d, 0xdf, 0x0e, 0x2f, 0x38, 0x3a, 0x77, 0xff, 0xff, 0x14, 0x67, 0xbf, 0xa9, 0x49, 0x8b, - 0x05, 0x34, 0x4c, 0x33, 0x81, 0x91, 0x9c, 0x54, 0xd6, 0xd8, 0x55, 0xb8, 0x44, 0x9b, 0xc4, 0x02, - 0x22, 0xc3, 0x5e, 0x81, 0xab, 0x91, 0x0d, 0xbd, 0x88, 0x5c, 0x47, 0xa4, 0x66, 0x51, 0xe4, 0xe5, - 0x12, 0x32, 0xbb, 0xfb, 0x83, 0x3f, 0xf9, 0xc5, 0xcd, 0xcc, 0x3f, 0xfb, 0xc5, 0xcd, 0xcc, 0xbf, - 0xfb, 0xc5, 0xcd, 0xb5, 0x3f, 0xfc, 0x0f, 0x37, 0x33, 0x3f, 0x7e, 0x67, 0x62, 0x87, 0x27, 0xf3, - 0xe3, 0x07, 0x23, 0x6f, 0xfa, 0xee, 0xd4, 0x08, 0x7d, 0xfb, 0x9c, 0xef, 0x56, 0x51, 0xc2, 0xb5, - 0xde, 0x9d, 0x9d, 0x4e, 0xde, 0x9d, 0x1d, 0xbf, 0x8b, 0xeb, 0xe6, 0xb8, 0x30, 0xf3, 0xbd, 0xd0, - 0x7b, 0xf4, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x7c, 0x5a, 0x02, 0x16, 0xd7, 0x8b, 0x00, 0x00, + 0xb8, 0xf6, 0xd7, 0x02, 0x6b, 0x03, 0x86, 0x01, 0xc3, 0x86, 0x61, 0x63, 0x80, 0xf5, 0x7e, 0xd9, + 0x03, 0xff, 0xac, 0xe1, 0x07, 0x0c, 0x1b, 0xf6, 0x62, 0x6d, 0x60, 0x6d, 0x8f, 0x7f, 0x6d, 0xd8, + 0x80, 0x0d, 0x18, 0xf0, 0x8f, 0x8d, 0x73, 0x22, 0x32, 0x33, 0x92, 0xa4, 0xa4, 0xee, 0x9e, 0x59, + 0xc0, 0x3f, 0x55, 0x8c, 0xf3, 0x88, 0x77, 0x9c, 0x38, 0xe7, 0xc4, 0x89, 0x48, 0x80, 0x99, 0x63, + 0xb8, 0x0f, 0x66, 0xbe, 0x17, 0x7a, 0x2c, 0x87, 0xbf, 0xaf, 0xbf, 0x33, 0xb1, 0xc3, 0x93, 0xf9, + 0xf1, 0x83, 0x91, 0x37, 0x7d, 0x77, 0xe2, 0x4d, 0xbc, 0x77, 0x09, 0x79, 0x3c, 0x1f, 0x53, 0x8a, + 0x12, 0xf4, 0x8b, 0x33, 0x5d, 0x07, 0xc7, 0x1b, 0x9d, 0x8a, 0xdf, 0x9b, 0xa1, 0x3d, 0xb5, 0x82, + 0xd0, 0x98, 0xce, 0x38, 0x40, 0xfd, 0xa3, 0x0c, 0xe4, 0x86, 0x17, 0x33, 0x8b, 0x6d, 0xc0, 0xba, + 0x6d, 0xd6, 0x33, 0x3b, 0x99, 0x7b, 0x79, 0x6d, 0xdd, 0x36, 0xd9, 0x0e, 0x54, 0x5c, 0x2f, 0xec, + 0xcd, 0x1d, 0xc7, 0x38, 0x76, 0xac, 0xfa, 0xfa, 0x4e, 0xe6, 0x5e, 0x49, 0x93, 0x41, 0xec, 0x15, + 0x28, 0x1b, 0xf3, 0xd0, 0xd3, 0x6d, 0x77, 0xe4, 0xd7, 0xb3, 0x84, 0x2f, 0x21, 0xa0, 0xe3, 0x8e, + 0x7c, 0xb6, 0x0d, 0xf9, 0x33, 0xdb, 0x0c, 0x4f, 0xea, 0x39, 0xca, 0x91, 0x27, 0x10, 0x1a, 0x8c, + 0x0c, 0xc7, 0xaa, 0xe7, 0x39, 0x94, 0x12, 0x08, 0x0d, 0xa9, 0x90, 0xc2, 0x4e, 0xe6, 0x5e, 0x59, + 0xe3, 0x09, 0x76, 0x13, 0xc0, 0x72, 0xe7, 0xd3, 0x67, 0x86, 0x33, 0xb7, 0x82, 0x7a, 0x91, 0x50, + 0x12, 0x44, 0xfd, 0x01, 0x94, 0xa7, 0xc1, 0x64, 0xdf, 0x32, 0x4c, 0xcb, 0x67, 0x57, 0xa1, 0x38, + 0x0d, 0x26, 0x7a, 0x68, 0x4c, 0x44, 0x13, 0x0a, 0xd3, 0x60, 0x32, 0x34, 0x26, 0xec, 0x1a, 0x94, + 0x08, 0x71, 0x31, 0xe3, 0x6d, 0xc8, 0x6b, 0x48, 0x88, 0x2d, 0x56, 0x7f, 0xbf, 0x00, 0xc5, 0xae, + 0x1d, 0x5a, 0xbe, 0xe1, 0xb0, 0x2b, 0x50, 0xb0, 0x03, 0x77, 0xee, 0x38, 0xc4, 0x5e, 0xd2, 0x44, + 0x8a, 0x5d, 0x81, 0xbc, 0xfd, 0xc9, 0x33, 0xc3, 0xe1, 0xbc, 0xfb, 0x6b, 0x1a, 0x4f, 0xb2, 0x3a, + 0x14, 0xec, 0xf7, 0x3f, 0x42, 0x44, 0x56, 0x20, 0x44, 0x9a, 0x30, 0x8f, 0x1e, 0x22, 0x26, 0x17, + 0x63, 0x28, 0x4d, 0x98, 0x8f, 0x3e, 0x40, 0x0c, 0xb6, 0x3e, 0x4b, 0x18, 0x4a, 0x63, 0x29, 0x73, + 0x2a, 0x05, 0x3b, 0xa0, 0x86, 0xa5, 0xcc, 0xa3, 0x52, 0xe6, 0xbc, 0x94, 0xa2, 0x40, 0x88, 0x34, + 0x61, 0x78, 0x29, 0xa5, 0x18, 0x13, 0x97, 0x32, 0xe7, 0xa5, 0x94, 0x77, 0x32, 0xf7, 0x72, 0x84, + 0xe1, 0xa5, 0x6c, 0x43, 0xce, 0x44, 0x38, 0xec, 0x64, 0xee, 0x65, 0xf6, 0xd7, 0x34, 0x4a, 0x21, + 0x34, 0x40, 0x68, 0x05, 0x3b, 0x18, 0xa1, 0x81, 0x80, 0x1e, 0x23, 0xb4, 0x8a, 0xbd, 0x81, 0xd0, + 0x63, 0x01, 0x1d, 0x23, 0xb4, 0xb6, 0x93, 0xb9, 0xb7, 0x8e, 0x50, 0x4c, 0xb1, 0xeb, 0x50, 0x34, + 0x8d, 0xd0, 0x42, 0xc4, 0x86, 0x68, 0x72, 0x04, 0x40, 0x1c, 0xce, 0x38, 0xc4, 0x6d, 0x8a, 0x46, + 0x47, 0x00, 0xa6, 0x42, 0x05, 0xc9, 0x22, 0xbc, 0x22, 0xf0, 0x32, 0x90, 0x7d, 0x08, 0x55, 0xd3, + 0x1a, 0xd9, 0x53, 0xc3, 0xe1, 0x6d, 0xda, 0xda, 0xc9, 0xdc, 0xab, 0x3c, 0xdc, 0x7c, 0x40, 0x6b, + 0x22, 0xc6, 0xec, 0xaf, 0x69, 0x29, 0x32, 0xf6, 0x09, 0xd4, 0x44, 0xfa, 0xfd, 0x87, 0xd4, 0xb1, + 0x8c, 0xf8, 0x94, 0x14, 0xdf, 0xfb, 0x0f, 0x3f, 0xd9, 0x5f, 0xd3, 0xd2, 0x84, 0xec, 0x0e, 0x54, + 0xe3, 0x25, 0x82, 0x8c, 0x97, 0x44, 0xad, 0x52, 0x50, 0x6c, 0xd6, 0x57, 0x81, 0xe7, 0x22, 0xc1, + 0xb6, 0xe8, 0xb7, 0x08, 0xc0, 0x76, 0x00, 0x4c, 0x6b, 0x6c, 0xcc, 0x9d, 0x10, 0xd1, 0x97, 0x45, + 0x07, 0x4a, 0x30, 0x76, 0x13, 0xca, 0xf3, 0x19, 0xb6, 0xf2, 0x89, 0xe1, 0xd4, 0xaf, 0x08, 0x82, + 0x04, 0x84, 0xb9, 0xe3, 0x3c, 0x47, 0xec, 0x55, 0x31, 0xba, 0x11, 0x00, 0x87, 0xf7, 0x99, 0x35, + 0x42, 0x54, 0x5d, 0x14, 0x2c, 0xd2, 0xb8, 0x8a, 0xec, 0x60, 0xd7, 0x76, 0xeb, 0xd7, 0x68, 0x06, + 0xf3, 0x04, 0xbb, 0x01, 0xd9, 0xc0, 0x1f, 0xd5, 0xaf, 0x53, 0xfb, 0x81, 0xb7, 0xbf, 0x7d, 0x3e, + 0xf3, 0x35, 0x04, 0xef, 0x16, 0x21, 0x4f, 0xab, 0x49, 0xbd, 0x01, 0xa5, 0x43, 0xc3, 0x37, 0xa6, + 0x9a, 0x35, 0x66, 0x0a, 0x64, 0x67, 0x5e, 0x20, 0xd6, 0x11, 0xfe, 0x54, 0xbb, 0x50, 0x78, 0x62, + 0xf8, 0x88, 0x63, 0x90, 0x73, 0x8d, 0xa9, 0x45, 0xc8, 0xb2, 0x46, 0xbf, 0x71, 0xed, 0x04, 0x17, + 0x41, 0x68, 0x4d, 0x85, 0x90, 0x10, 0x29, 0x84, 0x4f, 0x1c, 0xef, 0x58, 0xac, 0x91, 0x92, 0x26, + 0x52, 0xea, 0xff, 0x93, 0x81, 0x42, 0xd3, 0x73, 0x30, 0xbb, 0xab, 0x50, 0xf4, 0x2d, 0x47, 0x4f, + 0x8a, 0x2b, 0xf8, 0x96, 0x73, 0xe8, 0x05, 0x88, 0x18, 0x79, 0x1c, 0xc1, 0x57, 0x6d, 0x61, 0xe4, + 0x11, 0x22, 0xaa, 0x40, 0x56, 0xaa, 0xc0, 0x35, 0x28, 0x85, 0xc7, 0x8e, 0x4e, 0xf0, 0x1c, 0xc1, + 0x8b, 0xe1, 0xb1, 0xd3, 0x43, 0xd4, 0x55, 0x28, 0x9a, 0xc7, 0x1c, 0x93, 0x27, 0x4c, 0xc1, 0x3c, + 0x46, 0x84, 0xfa, 0x29, 0x94, 0x35, 0xe3, 0x4c, 0x54, 0xe3, 0x32, 0x14, 0x30, 0x03, 0x21, 0xff, + 0x72, 0x5a, 0x3e, 0x3c, 0x76, 0x3a, 0x26, 0x82, 0xb1, 0x12, 0xb6, 0x49, 0x75, 0xc8, 0x69, 0xf9, + 0x91, 0xe7, 0x74, 0x4c, 0x75, 0x08, 0xd0, 0xf4, 0x7c, 0xff, 0x3b, 0x37, 0x61, 0x1b, 0xf2, 0xa6, + 0x35, 0x0b, 0x4f, 0xb8, 0xe8, 0xd0, 0x78, 0x42, 0xbd, 0x0f, 0x25, 0x1c, 0x97, 0xae, 0x1d, 0x84, + 0xec, 0x26, 0xe4, 0x1c, 0x3b, 0x08, 0xeb, 0x99, 0x9d, 0xec, 0xc2, 0xa8, 0x11, 0x5c, 0xdd, 0x81, + 0xd2, 0x81, 0x71, 0xfe, 0x04, 0x47, 0x0e, 0x73, 0xa3, 0x21, 0x14, 0x43, 0x22, 0xc6, 0xb3, 0x0a, + 0x30, 0x34, 0xfc, 0x89, 0x15, 0x92, 0xa4, 0xfb, 0x6f, 0x19, 0xa8, 0x0c, 0xe6, 0xc7, 0x5f, 0xcf, + 0x2d, 0xff, 0x02, 0xeb, 0x7c, 0x0f, 0xb2, 0xe1, 0xc5, 0x8c, 0x38, 0x36, 0x1e, 0x5e, 0xe1, 0xd9, + 0x4b, 0xf8, 0x07, 0xc8, 0xa4, 0x21, 0x09, 0x36, 0xc2, 0xf5, 0x4c, 0x2b, 0xea, 0x83, 0xbc, 0x56, + 0xc0, 0x64, 0xc7, 0xc4, 0xed, 0xc2, 0x9b, 0x89, 0x51, 0x58, 0xf7, 0x66, 0x6c, 0x07, 0xf2, 0xa3, + 0x13, 0xdb, 0x31, 0x69, 0x00, 0xd2, 0x75, 0xe6, 0x08, 0x1c, 0x25, 0xdf, 0x3b, 0xd3, 0x03, 0xfb, + 0x27, 0x91, 0xf8, 0x2f, 0xfa, 0xde, 0xd9, 0xc0, 0xfe, 0x89, 0xa5, 0x0e, 0xc5, 0x1e, 0x04, 0x50, + 0x18, 0x34, 0x1b, 0xdd, 0x86, 0xa6, 0xac, 0xe1, 0xef, 0xf6, 0x17, 0x9d, 0xc1, 0x70, 0xa0, 0x64, + 0xd8, 0x06, 0x40, 0xaf, 0x3f, 0xd4, 0x45, 0x7a, 0x9d, 0x15, 0x60, 0xbd, 0xd3, 0x53, 0xb2, 0x48, + 0x83, 0xf0, 0x4e, 0x4f, 0xc9, 0xb1, 0x22, 0x64, 0x1b, 0xbd, 0x2f, 0x95, 0x3c, 0xfd, 0xe8, 0x76, + 0x95, 0x82, 0xfa, 0x3f, 0xd7, 0xa1, 0xdc, 0x3f, 0xfe, 0xca, 0x1a, 0x85, 0xd8, 0x66, 0x9c, 0xa5, + 0x96, 0xff, 0xcc, 0xf2, 0xa9, 0xd9, 0x59, 0x4d, 0xa4, 0xb0, 0x21, 0xe6, 0x31, 0x35, 0x2e, 0xab, + 0xad, 0x9b, 0xc7, 0x44, 0x37, 0x3a, 0xb1, 0xa6, 0x06, 0x35, 0x0e, 0xe9, 0x28, 0x85, 0xab, 0xc2, + 0x3b, 0xfe, 0x8a, 0x9a, 0x97, 0xd5, 0xf0, 0x27, 0xbb, 0x05, 0x15, 0x9e, 0x87, 0x3c, 0xbf, 0x80, + 0x83, 0x16, 0x27, 0x5f, 0x41, 0x9e, 0x7c, 0xc4, 0x49, 0xb9, 0x72, 0xa4, 0xd8, 0xdb, 0x38, 0xa8, + 0x27, 0x66, 0xb4, 0x77, 0xfc, 0x15, 0xc7, 0x96, 0xf8, 0x8c, 0xf6, 0x8e, 0xbf, 0x22, 0xd4, 0x5b, + 0xb0, 0x15, 0xcc, 0x8f, 0x83, 0x91, 0x6f, 0xcf, 0x42, 0xdb, 0x73, 0x39, 0x4d, 0x99, 0x68, 0x14, + 0x19, 0x41, 0xc4, 0xf7, 0xa0, 0x34, 0x9b, 0x1f, 0xeb, 0xb6, 0x3b, 0xf6, 0x48, 0xec, 0x57, 0x1e, + 0xd6, 0xf8, 0xc0, 0x1c, 0xce, 0x8f, 0x3b, 0xee, 0xd8, 0xd3, 0x8a, 0x33, 0xfe, 0x83, 0xa9, 0x50, + 0x73, 0xbd, 0x50, 0x47, 0x55, 0x41, 0x9f, 0x5a, 0xa1, 0x41, 0xfb, 0x01, 0xdf, 0xf0, 0xbb, 0xde, + 0xe8, 0xf4, 0xc0, 0x0a, 0x0d, 0x76, 0x1f, 0x4a, 0x81, 0x6b, 0xcc, 0x82, 0x13, 0x2f, 0xa4, 0x8d, + 0xa1, 0xf2, 0x70, 0x43, 0xcc, 0x1d, 0x01, 0xd5, 0x62, 0xbc, 0x7a, 0x17, 0x8a, 0xa2, 0x0c, 0xd4, + 0x13, 0x42, 0xcb, 0x35, 0xdc, 0x50, 0x8f, 0x15, 0x8c, 0x12, 0x07, 0x74, 0x4c, 0xf5, 0x67, 0x19, + 0x50, 0x06, 0x52, 0xb5, 0xa9, 0xa0, 0x55, 0x52, 0xe6, 0x55, 0x00, 0x63, 0x34, 0xf2, 0xe6, 0x3c, + 0x1b, 0x3e, 0x19, 0xcb, 0x02, 0xd2, 0x31, 0xe5, 0xbe, 0xce, 0xa6, 0xfa, 0xfa, 0x35, 0xa8, 0x46, + 0x7c, 0x92, 0x80, 0xa8, 0x08, 0x58, 0xd4, 0xdb, 0xc1, 0x3c, 0x25, 0x25, 0x8a, 0xc1, 0x9c, 0x73, + 0x5f, 0x81, 0x02, 0x69, 0x23, 0x41, 0x34, 0x82, 0x3c, 0xa5, 0xfe, 0x69, 0x06, 0x6a, 0x1d, 0xd7, + 0xb4, 0xce, 0x07, 0x23, 0xc3, 0x8d, 0x3a, 0xd0, 0x0e, 0x74, 0x1b, 0x61, 0x7a, 0x30, 0x32, 0x5c, + 0xa1, 0x48, 0x54, 0xec, 0x20, 0xa6, 0xc3, 0x36, 0x70, 0x02, 0x2a, 0x6a, 0x9d, 0x72, 0x2c, 0x13, + 0x84, 0x0a, 0xbb, 0x0b, 0x9b, 0xc7, 0x96, 0xe3, 0xb9, 0x13, 0x3d, 0xf4, 0x74, 0xae, 0x11, 0xf1, + 0xb6, 0xd4, 0x38, 0x78, 0xe8, 0x0d, 0x49, 0x33, 0xda, 0x86, 0xfc, 0xcc, 0xf0, 0xc3, 0xa0, 0x9e, + 0xdb, 0xc9, 0xe2, 0x92, 0xa7, 0x04, 0x76, 0xb3, 0x1d, 0xe8, 0x73, 0xd7, 0xfe, 0x7a, 0xce, 0x9b, + 0x51, 0xd2, 0x4a, 0x76, 0x70, 0x44, 0x69, 0x76, 0x0f, 0x14, 0x5e, 0x32, 0x65, 0x2b, 0xcf, 0xc9, + 0x0d, 0x82, 0x53, 0xc6, 0x24, 0x18, 0xff, 0xd3, 0x3a, 0x94, 0xf6, 0xe6, 0xee, 0x08, 0x07, 0x83, + 0xdd, 0x86, 0xdc, 0x78, 0xee, 0x8e, 0xa8, 0x2d, 0xf1, 0xb6, 0x1b, 0xaf, 0x29, 0x8d, 0x90, 0x28, + 0xad, 0x0c, 0x7f, 0x82, 0x52, 0x6e, 0x49, 0x5a, 0x21, 0x9c, 0x46, 0x6e, 0x32, 0xd1, 0x47, 0x9e, + 0x3b, 0xb6, 0x27, 0xd4, 0xa2, 0xaa, 0x56, 0x36, 0x26, 0x93, 0x26, 0x01, 0x58, 0x03, 0x36, 0x13, + 0x34, 0x57, 0xd4, 0x72, 0x24, 0x98, 0xae, 0xf1, 0x9c, 0x1a, 0x93, 0x89, 0x6f, 0x4d, 0x8c, 0xd0, + 0xe2, 0xf4, 0x24, 0x9b, 0x6a, 0x31, 0x3b, 0xc9, 0xb7, 0xbf, 0x97, 0xe1, 0x75, 0xde, 0x73, 0x8c, + 0x09, 0x2b, 0x41, 0xae, 0xd7, 0xef, 0xb5, 0x95, 0x35, 0x56, 0x85, 0x52, 0xa7, 0x37, 0x6c, 0x6b, + 0xbd, 0x46, 0x57, 0xc9, 0x90, 0x70, 0x19, 0x36, 0x76, 0xbb, 0x6d, 0x65, 0x1d, 0x31, 0x4f, 0xfa, + 0xdd, 0xc6, 0xb0, 0xd3, 0x6d, 0x2b, 0x39, 0x8e, 0xd1, 0x3a, 0xcd, 0xa1, 0x52, 0x62, 0x0a, 0x54, + 0x0f, 0xb5, 0x7e, 0xeb, 0xa8, 0xd9, 0xd6, 0x7b, 0x47, 0xdd, 0xae, 0xa2, 0xb0, 0x4b, 0xb0, 0x19, + 0x43, 0xfa, 0x1c, 0xb8, 0x83, 0x2c, 0x4f, 0x1a, 0x5a, 0x43, 0x7b, 0xac, 0xfc, 0x90, 0x95, 0x20, + 0xdb, 0x78, 0xfc, 0x58, 0xf9, 0x29, 0xca, 0xa9, 0xf2, 0xd3, 0x4e, 0x4f, 0x7f, 0xd2, 0xe8, 0x1e, + 0xb5, 0x95, 0x9f, 0xae, 0x47, 0xe9, 0xbe, 0xd6, 0x6a, 0x6b, 0xca, 0x4f, 0x73, 0x6c, 0x0b, 0xaa, + 0x3f, 0xee, 0xf7, 0xda, 0x07, 0x8d, 0xc3, 0x43, 0xaa, 0xc8, 0x4f, 0x4b, 0xea, 0x7f, 0xce, 0x41, + 0x0e, 0xfb, 0x8a, 0xa9, 0x89, 0x4c, 0x8e, 0x3b, 0x11, 0x1b, 0xb7, 0x9b, 0xfb, 0x93, 0x3f, 0xbf, + 0xb5, 0xc6, 0xa5, 0xf1, 0x6b, 0x90, 0x75, 0xec, 0x90, 0x26, 0x4e, 0xbc, 0x92, 0x85, 0x06, 0xbb, + 0xbf, 0xa6, 0x21, 0x8e, 0xdd, 0x84, 0x0c, 0x17, 0xcb, 0xf1, 0xe2, 0x8c, 0xf6, 0xf5, 0xfd, 0x35, + 0x2d, 0x33, 0x63, 0x37, 0x20, 0xf3, 0x4c, 0xc8, 0xe8, 0x2a, 0xc7, 0xf3, 0x9d, 0x1d, 0xb1, 0xcf, + 0xd8, 0x0e, 0x64, 0x47, 0x1e, 0xd7, 0x4f, 0x63, 0x3c, 0xdf, 0xe7, 0x30, 0xff, 0x91, 0xe7, 0xb0, + 0xdb, 0x90, 0xf5, 0x8d, 0x33, 0x9a, 0x3b, 0xf1, 0x84, 0x88, 0x37, 0x52, 0x24, 0xf2, 0x8d, 0x33, + 0xac, 0xc4, 0x98, 0xa4, 0x5a, 0x5c, 0x89, 0x68, 0x46, 0x61, 0x31, 0x63, 0xb6, 0x03, 0x99, 0x33, + 0x92, 0x6b, 0xb1, 0x4a, 0xf6, 0xd4, 0x76, 0x4d, 0xef, 0x6c, 0x30, 0xb3, 0x46, 0x48, 0x71, 0xc6, + 0x5e, 0x87, 0x6c, 0x30, 0x3f, 0x26, 0xb9, 0x56, 0x79, 0xb8, 0xb5, 0xb4, 0x43, 0x61, 0x41, 0xc1, + 0xfc, 0x98, 0xdd, 0x85, 0xdc, 0xc8, 0xf3, 0x7d, 0x21, 0xdb, 0x94, 0xa8, 0xc2, 0xd1, 0xe6, 0x8c, + 0x2a, 0x2a, 0xe2, 0xb1, 0xc0, 0x90, 0x24, 0x5a, 0x4c, 0x94, 0xec, 0x8e, 0x58, 0x60, 0xc8, 0xee, + 0x88, 0x2d, 0x37, 0x25, 0xd7, 0xa2, 0x0d, 0x19, 0xf3, 0x41, 0x2c, 0x0e, 0xd2, 0xd4, 0x38, 0x27, + 0xfd, 0x37, 0x26, 0x8a, 0x76, 0x62, 0xac, 0xd3, 0xd4, 0x38, 0x67, 0x77, 0x20, 0xfb, 0xcc, 0x1a, + 0x91, 0x2a, 0x1c, 0x97, 0x26, 0x06, 0xe9, 0x09, 0x35, 0x0f, 0xd1, 0xb4, 0xb2, 0x3c, 0xc7, 0x24, + 0xad, 0x38, 0x1e, 0xcb, 0x3d, 0xcf, 0x31, 0x9f, 0xd0, 0x58, 0x12, 0x12, 0x15, 0x10, 0x63, 0x7e, + 0x8e, 0xf2, 0x4e, 0xe1, 0xaa, 0x82, 0x31, 0x3f, 0xef, 0x98, 0xb8, 0x15, 0xb9, 0xe6, 0x33, 0xd2, + 0x85, 0x33, 0x1a, 0xfe, 0x44, 0x63, 0x2d, 0xb0, 0x1c, 0x6b, 0x14, 0xda, 0xcf, 0xec, 0xf0, 0x82, + 0xb4, 0xdd, 0x8c, 0x26, 0x83, 0x76, 0x0b, 0x90, 0xb3, 0xce, 0x67, 0xbe, 0xba, 0x0f, 0x45, 0x51, + 0xca, 0x92, 0xc5, 0x77, 0x0d, 0x4a, 0x76, 0x80, 0xeb, 0x30, 0x08, 0x85, 0x26, 0x57, 0xb4, 0x83, + 0x26, 0x26, 0x51, 0x20, 0x9b, 0x46, 0x68, 0x88, 0xc5, 0x4b, 0xbf, 0xd5, 0x87, 0x00, 0x49, 0xb3, + 0xb0, 0x4e, 0x8e, 0xe5, 0x46, 0x4a, 0xa3, 0x63, 0xb9, 0x31, 0xcf, 0xba, 0xc4, 0x73, 0x0d, 0xca, + 0xb1, 0x9e, 0xce, 0xaa, 0x90, 0x31, 0xc4, 0x66, 0x9c, 0x31, 0xd4, 0x7b, 0xa8, 0x36, 0x47, 0x9a, + 0x78, 0x1a, 0x87, 0xa9, 0x68, 0x8b, 0xce, 0x1c, 0xab, 0xdf, 0x87, 0xaa, 0x66, 0x05, 0x73, 0x27, + 0x6c, 0x7a, 0x4e, 0xcb, 0x1a, 0xb3, 0xb7, 0x01, 0xe2, 0x74, 0x20, 0x74, 0xa6, 0x64, 0xee, 0xb6, + 0xac, 0xb1, 0x26, 0xe1, 0xd5, 0xff, 0x90, 0x23, 0xed, 0xb3, 0xc5, 0xd5, 0x3e, 0xa1, 0xdf, 0x65, + 0x24, 0xfd, 0x2e, 0xde, 0x7d, 0xd6, 0xd3, 0x3a, 0xee, 0x89, 0x6d, 0x9a, 0x96, 0x1b, 0xe9, 0xb2, + 0x3c, 0x85, 0x83, 0x6d, 0x38, 0x13, 0x21, 0xb0, 0x58, 0x54, 0xe8, 0x74, 0xe6, 0x5b, 0x41, 0xc0, + 0xb5, 0x28, 0xc3, 0x99, 0x44, 0x6b, 0x3b, 0xff, 0xa2, 0xb5, 0x7d, 0x0d, 0x4a, 0xb8, 0x01, 0x93, + 0x0d, 0x5a, 0xe0, 0xbd, 0x2f, 0x8c, 0x6d, 0xf6, 0x06, 0x14, 0x85, 0xf5, 0x20, 0x16, 0x95, 0x98, + 0x2e, 0x2d, 0x0e, 0xd4, 0x22, 0x2c, 0xab, 0xa3, 0xca, 0x39, 0x9d, 0x5a, 0x6e, 0x18, 0x69, 0x0d, + 0x22, 0xc9, 0xde, 0x82, 0xb2, 0xe7, 0xea, 0xdc, 0xc4, 0x10, 0xab, 0x4a, 0x4c, 0xdf, 0xbe, 0x7b, + 0x44, 0x50, 0xad, 0xe4, 0x89, 0x5f, 0x58, 0x15, 0xc7, 0x3b, 0xd3, 0x47, 0x86, 0x6f, 0xd2, 0xca, + 0x2a, 0x69, 0x45, 0xc7, 0x3b, 0x6b, 0x1a, 0xbe, 0xc9, 0xb5, 0xa8, 0xaf, 0xdd, 0xf9, 0x94, 0x56, + 0x53, 0x4d, 0x13, 0x29, 0x76, 0x03, 0xca, 0x23, 0x67, 0x1e, 0x84, 0x96, 0xbf, 0x7b, 0xc1, 0x8d, + 0x46, 0x2d, 0x01, 0x60, 0xbd, 0x66, 0xbe, 0x3d, 0x35, 0xfc, 0x0b, 0x5a, 0x3a, 0x25, 0x2d, 0x4a, + 0xd2, 0x56, 0x76, 0x6a, 0x9b, 0xe7, 0xdc, 0x72, 0xd4, 0x78, 0x02, 0xe9, 0x4f, 0xc8, 0xae, 0x0f, + 0x68, 0x7d, 0x94, 0xb4, 0x28, 0x49, 0xe3, 0x40, 0x3f, 0x69, 0x45, 0x94, 0x35, 0x91, 0x4a, 0x99, + 0x00, 0x5b, 0xcf, 0x35, 0x01, 0xd8, 0xa2, 0x16, 0xe6, 0xf9, 0xf6, 0xc4, 0x16, 0x3a, 0xd4, 0x25, + 0xae, 0x85, 0x71, 0x10, 0x11, 0x7c, 0x0c, 0xb5, 0x89, 0xe5, 0x5a, 0xbe, 0x11, 0x5a, 0xa6, 0x8e, + 0x72, 0x71, 0x9b, 0x3a, 0x4e, 0x0c, 0xf3, 0xe3, 0x08, 0x85, 0xb2, 0xa6, 0x3a, 0x91, 0x52, 0xea, + 0xd7, 0x50, 0x14, 0x63, 0x83, 0x9b, 0x23, 0xae, 0xbb, 0xb4, 0x5c, 0xe7, 0x9b, 0x23, 0xc2, 0xd9, + 0x6d, 0xa8, 0x89, 0x4a, 0x04, 0xa1, 0x6f, 0xbb, 0x13, 0x31, 0xeb, 0xaa, 0x1c, 0x38, 0x20, 0x18, + 0xea, 0x30, 0x38, 0x2f, 0x74, 0xe3, 0xd8, 0x76, 0x70, 0x7d, 0x67, 0x85, 0x6e, 0x36, 0x77, 0x9c, + 0x06, 0x07, 0xa9, 0x7d, 0x28, 0x45, 0x23, 0xf9, 0x2b, 0x29, 0x53, 0x9d, 0x41, 0x55, 0x6e, 0xe1, + 0xaf, 0xa6, 0x21, 0x5c, 0x47, 0x09, 0x42, 0xcf, 0xb7, 0xcc, 0xc8, 0x65, 0x64, 0x07, 0x03, 0x4a, + 0xab, 0xbf, 0x93, 0x81, 0x0a, 0xe9, 0x4a, 0x7d, 0xd2, 0x04, 0xd9, 0xdb, 0xc0, 0x46, 0xbe, 0x65, + 0x84, 0x96, 0x6e, 0x9d, 0x87, 0xbe, 0x21, 0x34, 0x22, 0xae, 0x56, 0x29, 0x1c, 0xd3, 0x46, 0x04, + 0x57, 0x8a, 0x6e, 0x41, 0x65, 0x66, 0xf8, 0x41, 0xa4, 0x8d, 0xf3, 0xd2, 0x81, 0x83, 0x84, 0x2e, + 0xac, 0xb8, 0x13, 0xdf, 0x98, 0xea, 0xa1, 0x77, 0x6a, 0xb9, 0xdc, 0x0e, 0xe1, 0x16, 0xd8, 0x06, + 0xc1, 0x87, 0x08, 0x26, 0x73, 0xe4, 0xdf, 0x64, 0xa0, 0x76, 0xc8, 0x27, 0xe8, 0xe7, 0xd6, 0x45, + 0x8b, 0x9b, 0xbd, 0xa3, 0x48, 0xb8, 0xe4, 0x34, 0xfa, 0xcd, 0x6e, 0x42, 0x65, 0x76, 0x6a, 0x5d, + 0xe8, 0x29, 0x13, 0xb1, 0x8c, 0xa0, 0x26, 0x89, 0x91, 0x37, 0xa1, 0xe0, 0x51, 0x43, 0xc4, 0x76, + 0x2c, 0x76, 0x31, 0xa9, 0x85, 0x9a, 0x20, 0x40, 0xdd, 0x31, 0xce, 0x4a, 0x56, 0x52, 0x45, 0x66, + 0x54, 0xfd, 0x6d, 0xc8, 0x23, 0x2a, 0xa8, 0xe7, 0xb9, 0xd2, 0x47, 0x09, 0xf6, 0x1e, 0xd4, 0x46, + 0xde, 0x74, 0xa6, 0x47, 0xec, 0x62, 0x63, 0x4e, 0x8b, 0xbf, 0x0a, 0x92, 0x1c, 0xf2, 0xbc, 0xd4, + 0x9f, 0x67, 0xa1, 0x44, 0x75, 0x10, 0x12, 0xd0, 0x36, 0xcf, 0x23, 0x09, 0x58, 0xd6, 0xf2, 0xb6, + 0x89, 0x1b, 0xcc, 0x4b, 0xf4, 0xd4, 0x58, 0xff, 0xcc, 0xca, 0xfa, 0xe7, 0x15, 0x28, 0x08, 0xe5, + 0x33, 0xc7, 0x45, 0xe4, 0xfc, 0xf9, 0xaa, 0x67, 0x7e, 0x95, 0xea, 0x89, 0x43, 0xc8, 0x69, 0xac, + 0x73, 0xdc, 0x8a, 0xb9, 0x14, 0x04, 0x02, 0xb5, 0x11, 0x22, 0xcb, 0xb7, 0x62, 0x5a, 0xbe, 0xd5, + 0xa1, 0xf8, 0xcc, 0x0e, 0x6c, 0x9c, 0x20, 0x25, 0x2e, 0x31, 0x44, 0x52, 0x1a, 0x86, 0xf2, 0xcb, + 0x86, 0x21, 0x6e, 0xb6, 0xe1, 0x4c, 0xb8, 0xbd, 0x14, 0x35, 0xbb, 0xe1, 0x4c, 0x3c, 0xf6, 0x3e, + 0x5c, 0x4e, 0xd0, 0xa2, 0x35, 0xa4, 0xae, 0x92, 0xeb, 0x4c, 0x63, 0x31, 0x25, 0xb5, 0x88, 0x0c, + 0xda, 0xfb, 0xb0, 0x25, 0xb1, 0xcc, 0x50, 0x13, 0x0b, 0x48, 0x3c, 0x96, 0xb5, 0xcd, 0x98, 0x9c, + 0x14, 0xb4, 0x80, 0xbd, 0x89, 0xfd, 0x34, 0x72, 0xe6, 0x26, 0x17, 0x36, 0xf3, 0xa9, 0x1b, 0xd4, + 0x6b, 0xd4, 0xc1, 0x9b, 0x11, 0xbc, 0xc9, 0xc1, 0xea, 0x3f, 0x5a, 0x87, 0xda, 0x9e, 0xe7, 0x5b, + 0xf6, 0xc4, 0x4d, 0x26, 0xe8, 0x92, 0xc5, 0x14, 0x4d, 0xda, 0x75, 0x69, 0xd2, 0xde, 0x82, 0xca, + 0x98, 0x33, 0xea, 0xe1, 0x31, 0x77, 0xcc, 0xe4, 0x34, 0x10, 0xa0, 0xe1, 0xb1, 0x83, 0xa2, 0x26, + 0x22, 0x20, 0xe6, 0x1c, 0x31, 0x47, 0x4c, 0xb8, 0x83, 0xb2, 0xef, 0xd1, 0x5e, 0x62, 0x5a, 0x8e, + 0x15, 0xf2, 0x91, 0xdc, 0x78, 0xf8, 0x6a, 0xa4, 0xbf, 0x48, 0x75, 0x7a, 0xa0, 0x59, 0xe3, 0x06, + 0x29, 0x7d, 0xb8, 0xb5, 0xb4, 0x88, 0x5c, 0xf0, 0x8a, 0x7d, 0xa8, 0xf0, 0x0d, 0x79, 0xb9, 0x58, + 0x53, 0x87, 0x50, 0x8e, 0xc1, 0xa8, 0xc1, 0x6b, 0x6d, 0xa1, 0xb5, 0xaf, 0xb1, 0x0a, 0x14, 0x9b, + 0x8d, 0x41, 0xb3, 0xd1, 0x6a, 0x2b, 0x19, 0x44, 0x0d, 0xda, 0x43, 0xae, 0xa9, 0xaf, 0xb3, 0x4d, + 0xa8, 0x60, 0xaa, 0xd5, 0xde, 0x6b, 0x1c, 0x75, 0x87, 0x4a, 0x96, 0xd5, 0xa0, 0xdc, 0xeb, 0xeb, + 0x8d, 0xe6, 0xb0, 0xd3, 0xef, 0x29, 0x39, 0xf5, 0x87, 0x50, 0x6a, 0x9e, 0x58, 0xa3, 0xd3, 0xe7, + 0xf5, 0x22, 0x39, 0x36, 0xac, 0xd1, 0xa9, 0xd0, 0xba, 0x17, 0x1c, 0x1b, 0xd6, 0xe8, 0x54, 0x6d, + 0x43, 0xf9, 0xd0, 0xf0, 0x43, 0x9b, 0xea, 0xf5, 0x09, 0xd4, 0xe2, 0x44, 0xcb, 0x1a, 0x47, 0xfa, + 0x08, 0x8b, 0x75, 0xf1, 0x18, 0xa5, 0xa5, 0x09, 0xd5, 0xb7, 0xa1, 0x2a, 0x03, 0xd8, 0x0d, 0xc8, + 0x9a, 0xd6, 0x78, 0x85, 0xbc, 0x45, 0xb0, 0xfa, 0x04, 0xaa, 0xcd, 0x68, 0x7f, 0x7d, 0x5e, 0xd5, + 0x1f, 0xc2, 0x06, 0x09, 0x87, 0xd1, 0x71, 0x24, 0x1d, 0xd6, 0x57, 0x48, 0x87, 0x2a, 0xd2, 0x34, + 0x8f, 0x85, 0x78, 0xf8, 0x10, 0x2a, 0x87, 0xbe, 0x37, 0xb3, 0xfc, 0x90, 0xb2, 0x55, 0x20, 0x7b, + 0x6a, 0x5d, 0x88, 0x5c, 0xf1, 0x67, 0xe2, 0x6f, 0x5a, 0x97, 0xfd, 0x4d, 0x0f, 0xa1, 0x14, 0xb1, + 0x7d, 0x63, 0x9e, 0x1f, 0xa0, 0x94, 0x25, 0x1e, 0xdb, 0x0a, 0xb0, 0xb0, 0x07, 0x00, 0xb3, 0x18, + 0x20, 0x3a, 0x2e, 0x32, 0x62, 0x44, 0xe6, 0x9a, 0x44, 0xa1, 0xbe, 0x0a, 0xc5, 0x27, 0xb6, 0x75, + 0x26, 0x9a, 0xff, 0xcc, 0xb6, 0xce, 0xa2, 0xe6, 0xe3, 0x6f, 0xf5, 0x7f, 0x94, 0xa1, 0x44, 0x4b, + 0xb1, 0xf5, 0x7c, 0x17, 0xdf, 0xb7, 0xd1, 0xf5, 0x76, 0xc4, 0x7a, 0xca, 0xad, 0xd0, 0x30, 0xf9, + 0xea, 0x7a, 0x15, 0x40, 0x12, 0x0b, 0x5c, 0xc8, 0x95, 0xc3, 0x58, 0x1a, 0xa0, 0x92, 0x44, 0xdb, + 0x56, 0xf0, 0xb5, 0x23, 0xac, 0xef, 0x04, 0xc0, 0x1e, 0x70, 0x15, 0x86, 0xec, 0x6d, 0xae, 0xe6, + 0x5d, 0x8a, 0x4c, 0x95, 0x63, 0xc7, 0x8a, 0x0c, 0x28, 0xd2, 0x6b, 0x30, 0x41, 0x22, 0xcf, 0xf2, + 0x03, 0x94, 0x6c, 0x74, 0x06, 0xa0, 0x45, 0x49, 0xf6, 0x06, 0xe4, 0x70, 0x3f, 0x10, 0x06, 0xcf, + 0xa5, 0xa8, 0x07, 0xa5, 0x0d, 0x4d, 0x23, 0x02, 0x76, 0x0f, 0x8a, 0x24, 0x85, 0x2c, 0x14, 0x4a, + 0x52, 0x6f, 0x47, 0xfb, 0x83, 0x16, 0xa1, 0xd9, 0x9b, 0x90, 0x1f, 0x9f, 0x5a, 0x17, 0x5c, 0x22, + 0xc5, 0x79, 0xa6, 0xd6, 0xac, 0xc6, 0x29, 0xd8, 0x1d, 0xd8, 0xf0, 0xad, 0xb1, 0x4e, 0x4e, 0x3f, + 0x14, 0x32, 0x41, 0x7d, 0x83, 0x64, 0x48, 0xd5, 0xb7, 0xc6, 0x4d, 0x04, 0x0e, 0x8f, 0x9d, 0x80, + 0xdd, 0x85, 0x02, 0xad, 0x1e, 0xd4, 0xf0, 0xa4, 0x92, 0xa3, 0xa5, 0xa8, 0x09, 0x2c, 0x7b, 0x1f, + 0x40, 0xe8, 0x91, 0xfa, 0xf1, 0x05, 0x39, 0xcb, 0xe3, 0xc5, 0x24, 0xcf, 0x7f, 0x59, 0xdb, 0x7c, + 0x03, 0xf2, 0x38, 0x49, 0x82, 0xfa, 0x55, 0xca, 0x79, 0x2b, 0x3d, 0x83, 0xa8, 0xa6, 0x84, 0x67, + 0xf7, 0xa0, 0x84, 0x13, 0x45, 0xc7, 0xe1, 0xa8, 0xcb, 0x8a, 0xb5, 0x98, 0x55, 0xb8, 0x89, 0x58, + 0x67, 0x83, 0xaf, 0x1d, 0x76, 0x1f, 0x72, 0x26, 0x2e, 0xe6, 0x6b, 0x94, 0xe3, 0x15, 0x69, 0x5c, + 0x50, 0x58, 0xb5, 0xac, 0x31, 0xe9, 0xfa, 0x44, 0xc3, 0xf6, 0x61, 0x03, 0xa7, 0xd1, 0x43, 0xd2, + 0x0b, 0xb0, 0xfb, 0xea, 0xd7, 0x89, 0xeb, 0xb5, 0x05, 0xae, 0x9e, 0x20, 0xa2, 0xce, 0x6e, 0xbb, + 0xa1, 0x7f, 0xa1, 0xd5, 0x5c, 0x19, 0xc6, 0xae, 0xa3, 0x41, 0xd6, 0xf5, 0x46, 0xa7, 0x96, 0x59, + 0x7f, 0x25, 0x52, 0x96, 0x78, 0x9a, 0x7d, 0x0a, 0x35, 0x9a, 0x58, 0x98, 0xc4, 0xc2, 0xeb, 0x37, + 0x48, 0x98, 0xca, 0x53, 0x26, 0x42, 0x69, 0x69, 0x4a, 0x14, 0xf1, 0x76, 0xa0, 0x87, 0xd6, 0x74, + 0xe6, 0xf9, 0xa8, 0x92, 0xbf, 0x1a, 0x39, 0xaa, 0x86, 0x11, 0x08, 0xf7, 0xec, 0xf8, 0x68, 0x4f, + 0xf7, 0xc6, 0xe3, 0xc0, 0x0a, 0xeb, 0x37, 0x69, 0xdd, 0x6c, 0x44, 0x27, 0x7c, 0x7d, 0x82, 0xd2, + 0x9e, 0x19, 0xe8, 0xe6, 0x85, 0x6b, 0x4c, 0xed, 0x51, 0xfd, 0x16, 0xd7, 0xfc, 0xed, 0xa0, 0xc5, + 0x01, 0xb2, 0xf2, 0xbd, 0x93, 0x52, 0xbe, 0x2f, 0x41, 0xde, 0x3c, 0xc6, 0xe5, 0xf8, 0x1a, 0x65, + 0x9b, 0x33, 0x8f, 0x3b, 0x26, 0x7b, 0x07, 0xca, 0xb3, 0x48, 0x04, 0xd6, 0x55, 0xd9, 0xc5, 0x10, + 0x4b, 0x46, 0x2d, 0xa1, 0x40, 0xab, 0x77, 0xcf, 0x32, 0xc2, 0xb9, 0x6f, 0xed, 0x39, 0xc6, 0xa4, + 0x7e, 0x9b, 0x72, 0x92, 0x41, 0x58, 0x3b, 0xc7, 0x9b, 0xd8, 0x23, 0x83, 0x56, 0xfe, 0x1d, 0xae, + 0xa2, 0x09, 0x48, 0xc7, 0x4c, 0x74, 0x56, 0x43, 0xe8, 0x5d, 0xaf, 0xcb, 0x3a, 0xab, 0xe1, 0x44, + 0x5e, 0xb9, 0xa4, 0x2f, 0xac, 0x99, 0x37, 0x3a, 0xa9, 0xdf, 0xa5, 0xf5, 0x56, 0x8b, 0xba, 0xa2, + 0x8d, 0xc0, 0xeb, 0x8f, 0x49, 0xe9, 0xa7, 0x1e, 0xfe, 0x70, 0x41, 0x90, 0xa5, 0x96, 0xa1, 0x24, + 0xf1, 0xf6, 0xd7, 0x64, 0x79, 0xb6, 0x9b, 0x27, 0x89, 0x7f, 0xfd, 0x87, 0xc0, 0x96, 0xe7, 0xc6, + 0xcb, 0xa4, 0x6a, 0x5e, 0x48, 0xd5, 0xef, 0xad, 0x7f, 0x92, 0x51, 0x9f, 0x40, 0x2d, 0x25, 0x34, + 0x56, 0xee, 0x0e, 0x5c, 0x8b, 0x33, 0xa6, 0xc2, 0x40, 0xe7, 0x89, 0x48, 0x43, 0xb7, 0xdd, 0x89, + 0xf0, 0x3e, 0x72, 0x0d, 0x9d, 0xd2, 0xea, 0x3f, 0xcb, 0x42, 0x75, 0xdf, 0x08, 0x4e, 0x0e, 0x8c, + 0xd9, 0x20, 0x34, 0xc2, 0x00, 0xa7, 0xd2, 0x89, 0x11, 0x9c, 0x4c, 0x8d, 0x19, 0xd7, 0xa7, 0x33, + 0xdc, 0xf1, 0x20, 0x60, 0xa8, 0x4c, 0xe3, 0x24, 0xc6, 0x64, 0xdf, 0x3d, 0xfc, 0x5c, 0x78, 0x15, + 0xe2, 0x34, 0x8a, 0xb0, 0xe0, 0x64, 0x3e, 0x1e, 0xc7, 0x45, 0x45, 0x49, 0x76, 0x07, 0x6a, 0xe2, + 0x27, 0x29, 0xd3, 0xe7, 0xe2, 0x18, 0x39, 0x0d, 0x64, 0x8f, 0xa0, 0x22, 0x00, 0xc3, 0x48, 0xe0, + 0x6e, 0xc4, 0xde, 0xa2, 0x04, 0xa1, 0xc9, 0x54, 0xec, 0x47, 0x70, 0x59, 0x4a, 0xee, 0x79, 0xfe, + 0xc1, 0xdc, 0x09, 0xed, 0x66, 0x4f, 0xa8, 0x23, 0xaf, 0x2c, 0xb1, 0x27, 0x24, 0xda, 0x6a, 0xce, + 0x74, 0x6d, 0x0f, 0x6c, 0x97, 0xe4, 0x77, 0x56, 0x4b, 0x03, 0x17, 0xa8, 0x8c, 0x73, 0x12, 0xdb, + 0x69, 0x2a, 0xe3, 0x1c, 0x17, 0xb6, 0x00, 0x1c, 0x58, 0xe1, 0x89, 0x67, 0x92, 0xda, 0x1a, 0x2f, + 0xec, 0x81, 0x8c, 0xd2, 0xd2, 0x94, 0xd8, 0x9d, 0x68, 0x12, 0x8e, 0xdc, 0x90, 0x94, 0xd7, 0xac, + 0x16, 0x25, 0x71, 0x4b, 0xf3, 0x0d, 0x77, 0x62, 0x05, 0xf5, 0xca, 0x4e, 0xf6, 0x5e, 0x46, 0x13, + 0x29, 0xf5, 0xaf, 0xad, 0x43, 0x9e, 0x8f, 0xe4, 0x2b, 0x50, 0x3e, 0x26, 0xe7, 0x3f, 0xda, 0xf6, + 0xc2, 0x49, 0x4f, 0x80, 0xde, 0x7c, 0xca, 0x35, 0x49, 0xe1, 0x15, 0xca, 0x68, 0xf4, 0x1b, 0xb3, + 0xf4, 0xe6, 0x21, 0x96, 0x95, 0x25, 0xa8, 0x48, 0x61, 0x25, 0x7c, 0xef, 0x8c, 0x66, 0x43, 0x8e, + 0x10, 0x51, 0x92, 0xce, 0x01, 0x68, 0x77, 0x44, 0xa6, 0x3c, 0xe1, 0x4a, 0x04, 0x68, 0xba, 0xe1, + 0xa2, 0x07, 0xab, 0xb0, 0xe4, 0xc1, 0x62, 0x37, 0x01, 0xf5, 0xd4, 0x91, 0xd5, 0x77, 0xad, 0x66, + 0x8f, 0x7a, 0xb8, 0xa4, 0x49, 0x10, 0x5c, 0x20, 0xa6, 0x37, 0xa3, 0x4e, 0xcd, 0x6b, 0xf8, 0x93, + 0x7d, 0x14, 0xcf, 0x4e, 0x6a, 0xa3, 0x30, 0x00, 0xc4, 0xee, 0x21, 0xcf, 0x63, 0x2d, 0x45, 0x87, + 0x39, 0xe1, 0x96, 0xc0, 0x0d, 0x00, 0xfc, 0xa9, 0xb6, 0x01, 0x34, 0xef, 0x2c, 0xb0, 0x42, 0x72, + 0xd5, 0x5e, 0xa5, 0x26, 0xa6, 0x8e, 0xfc, 0xbc, 0xb3, 0x43, 0x2f, 0x88, 0x6d, 0xe4, 0xf5, 0xd5, + 0x36, 0xb2, 0xfa, 0x2e, 0x14, 0x51, 0x5f, 0x30, 0x42, 0x83, 0xdd, 0x11, 0xde, 0x31, 0xae, 0xe5, + 0x08, 0x37, 0x61, 0x52, 0x86, 0xf0, 0x97, 0x75, 0xa3, 0x72, 0x89, 0xe7, 0x35, 0xc9, 0x0a, 0x8d, + 0xf7, 0x2a, 0x91, 0xa1, 0xd0, 0x40, 0x5e, 0x81, 0x32, 0x56, 0x8d, 0xce, 0x36, 0x84, 0x5c, 0x28, + 0xf9, 0xde, 0x59, 0x13, 0xd3, 0xea, 0xbf, 0xcd, 0x40, 0xa5, 0xef, 0x9b, 0xb8, 0x49, 0x0e, 0x66, + 0xd6, 0xe8, 0xa5, 0x26, 0x3d, 0xea, 0x2b, 0x9e, 0xe3, 0x18, 0x24, 0x8e, 0x85, 0x15, 0x18, 0x03, + 0xd8, 0xfb, 0x90, 0x1b, 0xa3, 0xd8, 0xcd, 0xca, 0x5a, 0xbc, 0x94, 0x7d, 0xf4, 0x1b, 0x05, 0xb1, + 0x46, 0xa4, 0xea, 0x6f, 0xc6, 0xe5, 0x93, 0x74, 0x96, 0xfd, 0xf3, 0x6b, 0x74, 0x6e, 0x37, 0x68, + 0x2a, 0x19, 0x56, 0x82, 0x5c, 0xab, 0x3d, 0x68, 0x72, 0xdd, 0x1d, 0xb5, 0xf8, 0x81, 0xbe, 0xd7, + 0xd1, 0x06, 0x43, 0x25, 0x47, 0x07, 0x81, 0x04, 0xe8, 0x36, 0x06, 0x43, 0xa5, 0xc4, 0x00, 0x0a, + 0x47, 0xbd, 0xce, 0x8f, 0x8e, 0xda, 0x8a, 0xa2, 0xfe, 0xcb, 0x0c, 0x40, 0xe2, 0x46, 0x66, 0x6f, + 0x41, 0xe5, 0x8c, 0x52, 0xba, 0x74, 0x82, 0x21, 0xb7, 0x11, 0x38, 0x9a, 0x74, 0xa9, 0x77, 0xa0, + 0x1a, 0x6f, 0x2b, 0xa8, 0x67, 0x2c, 0x1f, 0x65, 0x54, 0x62, 0xfc, 0xee, 0x05, 0x7b, 0x1b, 0x4a, + 0x1e, 0xb6, 0x03, 0x49, 0xb3, 0xb2, 0x92, 0x21, 0x35, 0x5f, 0x2b, 0x7a, 0x3c, 0x81, 0xfa, 0xc8, + 0xd8, 0x8f, 0xac, 0xfa, 0x98, 0x74, 0x0f, 0x41, 0x4d, 0xc7, 0x98, 0x07, 0x96, 0xc6, 0xf1, 0xb1, + 0x94, 0xce, 0x27, 0x52, 0x5a, 0xfd, 0x31, 0x6c, 0x0c, 0x8c, 0xe9, 0x8c, 0xcb, 0x72, 0x6a, 0x18, + 0x83, 0x1c, 0xce, 0x09, 0x31, 0xf5, 0xe8, 0x37, 0x2e, 0xba, 0x43, 0xcb, 0x1f, 0x59, 0x6e, 0xb4, + 0x46, 0xa3, 0x24, 0x8a, 0xdf, 0x23, 0x94, 0xe6, 0x9a, 0x77, 0x16, 0x89, 0xf3, 0x28, 0xad, 0xfe, + 0xcd, 0x0c, 0x54, 0xa4, 0x6a, 0xb0, 0x77, 0x21, 0x47, 0x8a, 0x6b, 0x46, 0x16, 0x84, 0x12, 0x01, + 0xff, 0xcd, 0x55, 0x1d, 0x24, 0x64, 0x77, 0x21, 0x1f, 0x84, 0x86, 0x1f, 0x9d, 0x48, 0x28, 0x12, + 0xc7, 0xae, 0x37, 0x77, 0x4d, 0x8d, 0xa3, 0x99, 0x0a, 0x59, 0xcb, 0x35, 0x85, 0x1f, 0x64, 0x99, + 0x0a, 0x91, 0xea, 0x0e, 0x94, 0xe3, 0xec, 0x71, 0x0a, 0x68, 0xfd, 0xa7, 0x03, 0x65, 0x8d, 0x95, + 0x21, 0xaf, 0x35, 0x7a, 0x8f, 0xdb, 0x4a, 0x46, 0xfd, 0x59, 0x06, 0x20, 0xe1, 0x62, 0x0f, 0x52, + 0xb5, 0xbd, 0xbe, 0x98, 0xeb, 0x03, 0xfa, 0x2b, 0x55, 0xf6, 0x06, 0x94, 0xe7, 0x2e, 0x01, 0x2d, + 0x53, 0xec, 0x44, 0x09, 0x00, 0xad, 0xad, 0x28, 0x9a, 0x67, 0xc1, 0xda, 0x7a, 0x66, 0x38, 0xea, + 0xf7, 0xa0, 0x1c, 0x67, 0x87, 0x06, 0xe4, 0x5e, 0xbf, 0xdb, 0xed, 0x3f, 0xed, 0xf4, 0x1e, 0x2b, + 0x6b, 0x98, 0x3c, 0xd4, 0xda, 0xcd, 0x76, 0x0b, 0x93, 0x19, 0x9c, 0xb3, 0xcd, 0x23, 0x4d, 0x6b, + 0xf7, 0x86, 0xba, 0xd6, 0x7f, 0xaa, 0xac, 0xab, 0xbf, 0x9b, 0x81, 0x5a, 0xdf, 0x41, 0xdb, 0xbd, + 0x69, 0xcc, 0x50, 0x33, 0x61, 0x3f, 0x80, 0xad, 0xe3, 0x39, 0x6a, 0xc7, 0x33, 0xc7, 0x18, 0x59, + 0x27, 0x9e, 0x63, 0x5a, 0xd1, 0x22, 0x4c, 0x9d, 0xb9, 0x08, 0xf7, 0xb0, 0x42, 0xc4, 0x87, 0x09, + 0x2d, 0xfb, 0x10, 0xaa, 0x33, 0xdf, 0x3b, 0xb6, 0xf4, 0xc0, 0x9b, 0xfb, 0x23, 0x6b, 0xc9, 0xac, + 0x4b, 0x78, 0x2b, 0x44, 0x37, 0x20, 0x32, 0xf5, 0x6f, 0xaf, 0x43, 0xb5, 0x65, 0x99, 0xf3, 0xd9, + 0x67, 0x9e, 0xed, 0x36, 0xc3, 0x73, 0xf6, 0x01, 0x54, 0x3d, 0x87, 0xbc, 0x0d, 0xba, 0x14, 0x6f, + 0xb0, 0x2a, 0x1f, 0xf0, 0xa8, 0x05, 0x14, 0x9d, 0xf0, 0x0e, 0x5c, 0xe2, 0x06, 0xbc, 0x70, 0x7d, + 0x9d, 0x73, 0x66, 0x5c, 0x33, 0x79, 0x4d, 0xe1, 0x28, 0xbe, 0x41, 0x13, 0xf9, 0xaf, 0xc1, 0xb6, + 0x44, 0x8e, 0x82, 0x85, 0xd3, 0x67, 0x97, 0xd6, 0xd8, 0x56, 0xcc, 0x1b, 0x47, 0x42, 0x7c, 0x06, + 0xdb, 0x51, 0x0d, 0x47, 0xbc, 0xf7, 0x38, 0x73, 0x4e, 0x36, 0x43, 0x52, 0xbd, 0x2b, 0x2a, 0xbc, + 0xe5, 0xc9, 0x40, 0xca, 0xeb, 0x7d, 0xb8, 0x6c, 0x62, 0xeb, 0x75, 0xde, 0xf9, 0xa7, 0x96, 0x35, + 0xd3, 0x1d, 0x23, 0x08, 0xc5, 0x61, 0x29, 0x23, 0xe4, 0x2e, 0xe2, 0x3e, 0xb7, 0xac, 0x59, 0xd7, + 0x08, 0x42, 0xf5, 0x77, 0x72, 0x50, 0xe6, 0xde, 0x07, 0xec, 0xae, 0x7b, 0x50, 0xf4, 0x8e, 0xbf, + 0xd2, 0xfd, 0xd8, 0x2a, 0x5f, 0x3a, 0x10, 0x2d, 0x78, 0xc7, 0x5f, 0x69, 0xd6, 0x98, 0xbd, 0x15, + 0x6d, 0x75, 0x68, 0xc1, 0xaf, 0xcb, 0xee, 0xf6, 0x48, 0xfd, 0x17, 0x5b, 0x1f, 0x9a, 0xa6, 0x8f, + 0xa0, 0x62, 0xbb, 0x81, 0xe5, 0x87, 0xdc, 0xe3, 0x52, 0x7c, 0xfe, 0x20, 0x70, 0x32, 0x72, 0xc2, + 0x3c, 0x82, 0x0a, 0xf7, 0xc0, 0x70, 0xa6, 0xd2, 0xf3, 0x99, 0x38, 0x19, 0x31, 0x7d, 0x0a, 0x1b, + 0x89, 0x98, 0x23, 0xbe, 0xf2, 0x73, 0xf9, 0x6a, 0x31, 0x25, 0xb1, 0x3e, 0x84, 0x2b, 0xc1, 0xa9, + 0x3d, 0xd3, 0x45, 0x4d, 0x3d, 0x97, 0x4e, 0x2a, 0xf4, 0xd9, 0xa9, 0x38, 0x21, 0x60, 0x88, 0xed, + 0x10, 0xb2, 0xef, 0xf6, 0xe6, 0x8e, 0x73, 0x78, 0xca, 0xde, 0x84, 0x2d, 0x41, 0x3e, 0x3b, 0x8d, + 0xe6, 0x0a, 0x19, 0xa5, 0x79, 0x6d, 0x83, 0x23, 0x0e, 0x4f, 0x85, 0x26, 0xf7, 0x31, 0xd4, 0xf9, + 0x19, 0xbd, 0x68, 0x94, 0x31, 0x1e, 0x5b, 0xa3, 0x50, 0x47, 0xd5, 0x41, 0x1c, 0x27, 0x5c, 0x26, + 0x3c, 0x77, 0x23, 0x35, 0x08, 0x4b, 0x92, 0xef, 0x21, 0x5c, 0x11, 0x83, 0x7a, 0xa1, 0x87, 0x74, + 0xa6, 0x87, 0x4c, 0xa8, 0xed, 0xd7, 0xe4, 0x51, 0xbd, 0xe0, 0xe7, 0x7d, 0x9a, 0x77, 0xd6, 0x31, + 0x71, 0x22, 0x08, 0xd2, 0x68, 0x62, 0x86, 0xe7, 0x7a, 0x72, 0x08, 0xc1, 0x38, 0x32, 0x1e, 0xf7, + 0x8e, 0x79, 0xae, 0xfe, 0x9d, 0x0c, 0x94, 0x79, 0xeb, 0x70, 0x22, 0xbc, 0x06, 0xd9, 0x17, 0x4c, + 0x02, 0xc4, 0xb1, 0xfb, 0xb0, 0x65, 0x98, 0xa6, 0x68, 0x87, 0x65, 0xf2, 0x96, 0x70, 0xa9, 0xb3, + 0x69, 0x98, 0x66, 0x43, 0xc0, 0xa9, 0x0d, 0xf7, 0x40, 0xb1, 0x03, 0x3d, 0x32, 0x73, 0x93, 0x83, + 0xff, 0x92, 0xb6, 0x61, 0x07, 0xc2, 0xca, 0xe5, 0x4e, 0xee, 0xd4, 0xbc, 0xca, 0xbd, 0x78, 0x5e, + 0xa9, 0x0c, 0x40, 0xb3, 0x48, 0xc4, 0x34, 0xc3, 0xf3, 0xcf, 0x72, 0xa5, 0x8c, 0x02, 0xea, 0x3f, + 0xae, 0x40, 0xa5, 0xe1, 0x1a, 0xce, 0xc5, 0x4f, 0x2c, 0x8a, 0x5a, 0x20, 0x97, 0xe7, 0x6c, 0x2e, + 0x7a, 0x9a, 0x1f, 0xb8, 0x95, 0x09, 0x42, 0x35, 0xbb, 0x05, 0x15, 0x6f, 0x1e, 0xc6, 0x78, 0x7e, + 0x04, 0x07, 0x1c, 0x44, 0x04, 0x31, 0x7f, 0xec, 0x4e, 0x8f, 0xf8, 0x49, 0xf9, 0x4f, 0xf8, 0x63, + 0x85, 0x30, 0xe6, 0x27, 0x82, 0xdb, 0x50, 0x0b, 0xed, 0xa9, 0x45, 0xa7, 0x8e, 0xf3, 0xa9, 0x65, + 0xf2, 0xd0, 0x48, 0x1e, 0x6d, 0xd7, 0x14, 0x30, 0xcc, 0x65, 0x6a, 0x4d, 0x3d, 0xff, 0x82, 0xe7, + 0x52, 0xe0, 0xb9, 0x70, 0x10, 0xe5, 0xf2, 0x36, 0xb0, 0x33, 0xc3, 0x0e, 0xf5, 0x74, 0x56, 0x5c, + 0x09, 0x57, 0x10, 0x33, 0x94, 0xb3, 0xbb, 0x02, 0x05, 0xd3, 0x0e, 0x4e, 0x3b, 0x7d, 0xa1, 0x80, + 0x8b, 0x14, 0xb6, 0x25, 0x18, 0x19, 0xb8, 0xff, 0x87, 0x16, 0x57, 0x16, 0xb3, 0x5a, 0x19, 0x21, + 0xbb, 0x08, 0xc0, 0xfd, 0xc3, 0xb5, 0xc2, 0x33, 0xcf, 0x47, 0x4e, 0xae, 0x5f, 0x27, 0x00, 0xdc, + 0x67, 0x91, 0x14, 0x0b, 0xa2, 0x29, 0x9e, 0xd5, 0xe2, 0x34, 0x6a, 0xae, 0x7c, 0xba, 0x13, 0xb6, + 0xca, 0xab, 0x9f, 0x40, 0xd8, 0x1d, 0xd8, 0xa0, 0xea, 0x93, 0xfe, 0x8d, 0x6d, 0xa0, 0xb9, 0x9b, + 0xd5, 0xaa, 0x08, 0x25, 0xb3, 0x1d, 0xa9, 0x3e, 0x85, 0x6b, 0xa9, 0xf6, 0xe9, 0x86, 0xef, 0x1b, + 0x17, 0xfa, 0xd4, 0xf8, 0xca, 0xf3, 0xc9, 0xc5, 0x92, 0xd5, 0xae, 0xc8, 0xdd, 0xd6, 0x40, 0xf4, + 0x01, 0x62, 0x9f, 0xcb, 0x6a, 0xbb, 0x9e, 0x4f, 0xfe, 0x97, 0x95, 0xac, 0x88, 0x25, 0x67, 0x01, + 0x0d, 0x30, 0x19, 0x03, 0x01, 0x8f, 0xd2, 0xd4, 0x2a, 0x04, 0xdb, 0x25, 0x10, 0xaa, 0xc3, 0xc1, + 0x23, 0x2e, 0x96, 0xb7, 0x44, 0xc8, 0xd4, 0x23, 0x12, 0xb8, 0x1c, 0x71, 0x62, 0x19, 0x26, 0x9d, + 0xbc, 0x11, 0x62, 0xdf, 0x32, 0xe8, 0x5c, 0x3b, 0x78, 0xa4, 0xcf, 0xe6, 0x21, 0x0f, 0xaf, 0xd4, + 0xf2, 0xc1, 0xa3, 0xc3, 0x79, 0x28, 0xc0, 0x13, 0x2b, 0xa4, 0x83, 0x36, 0x02, 0x3f, 0xb6, 0x42, + 0xd4, 0x69, 0x83, 0x47, 0x91, 0xbf, 0xf9, 0xb2, 0xe8, 0xdb, 0x47, 0xc2, 0xa1, 0xac, 0x42, 0x2d, + 0x46, 0xea, 0xd3, 0x39, 0x8f, 0xa7, 0xcc, 0x6a, 0x95, 0x88, 0xe0, 0x60, 0xee, 0xe0, 0xc0, 0x8e, + 0x8c, 0xd1, 0x89, 0xa5, 0xfb, 0x58, 0x95, 0xab, 0x7c, 0xe8, 0x08, 0xa2, 0x61, 0x6d, 0x5e, 0x01, + 0x9e, 0xd0, 0x4f, 0xec, 0x90, 0xfc, 0x40, 0x59, 0xad, 0x44, 0x80, 0x7d, 0x3b, 0xc4, 0x75, 0xcc, + 0x91, 0x62, 0x06, 0x52, 0x16, 0xd7, 0x88, 0x68, 0x93, 0x10, 0x07, 0x04, 0xa7, 0x8c, 0xee, 0x81, + 0x92, 0xa2, 0xc5, 0xfc, 0xae, 0x13, 0xe9, 0x86, 0x44, 0x8a, 0xb9, 0xde, 0x05, 0xce, 0xac, 0xe3, + 0xd4, 0xe3, 0x79, 0xbe, 0xc2, 0x8d, 0x41, 0x02, 0xb7, 0xec, 0xe0, 0x94, 0x72, 0xbc, 0x03, 0x1b, + 0x12, 0x1d, 0xe6, 0x77, 0x83, 0xcf, 0x8c, 0x98, 0x2c, 0x55, 0x47, 0xdf, 0x9a, 0x7a, 0xa1, 0x68, + 0xe6, 0xab, 0x52, 0x1d, 0x35, 0x82, 0xa7, 0xeb, 0x28, 0x68, 0x31, 0xcf, 0x9b, 0x52, 0x1d, 0x39, + 0x29, 0xe6, 0xfa, 0x1a, 0x54, 0xcf, 0x7c, 0x3b, 0x0c, 0x2d, 0x97, 0x2f, 0xfe, 0x5b, 0xbc, 0x63, + 0x05, 0x8c, 0x56, 0xff, 0x6b, 0x50, 0xe5, 0x3d, 0x2f, 0xe4, 0xdb, 0x0e, 0x27, 0x11, 0xb0, 0x48, + 0x40, 0x88, 0xde, 0x98, 0xda, 0x2e, 0x39, 0x7b, 0xb2, 0x5a, 0x99, 0x43, 0xd0, 0x26, 0x96, 0xd0, + 0xc6, 0x39, 0xb9, 0x7c, 0x12, 0xb4, 0x71, 0x4e, 0x4b, 0x72, 0x66, 0x3b, 0x0e, 0x5f, 0xf8, 0xb7, + 0xc5, 0x92, 0x44, 0x08, 0xad, 0xfb, 0x18, 0x4d, 0xa5, 0xdf, 0x91, 0xd0, 0x54, 0x36, 0x4e, 0x1c, + 0x42, 0x63, 0xd1, 0xaf, 0x8b, 0x89, 0x83, 0x00, 0x2c, 0x39, 0x41, 0x1a, 0xe7, 0xe4, 0xd0, 0x89, + 0x91, 0xc6, 0xb9, 0x90, 0x5b, 0x98, 0x2b, 0xf1, 0xbe, 0x11, 0xcb, 0x2d, 0x04, 0x21, 0xb7, 0x4c, + 0x60, 0x9c, 0xd7, 0xef, 0xa5, 0x09, 0x8c, 0x73, 0x32, 0xc4, 0x2c, 0xc3, 0xe4, 0x15, 0x7f, 0x93, + 0x67, 0x8f, 0x00, 0xaa, 0xf7, 0x0e, 0x54, 0x83, 0x47, 0x7a, 0x82, 0xbf, 0xcf, 0xd9, 0x83, 0x47, + 0x5a, 0x44, 0x71, 0x07, 0x36, 0xe2, 0xa9, 0xc1, 0x69, 0xde, 0xe2, 0x03, 0x6f, 0x8a, 0xa9, 0x41, + 0x07, 0x95, 0x3f, 0xcd, 0xc0, 0xf5, 0x3e, 0xf9, 0xaa, 0x48, 0xfc, 0x1f, 0x58, 0x41, 0x60, 0x4c, + 0xac, 0x3d, 0xcf, 0xdf, 0x9b, 0xff, 0xe4, 0x27, 0x17, 0xec, 0x1e, 0x6c, 0x1e, 0x1a, 0xbe, 0xe5, + 0x86, 0xf1, 0x11, 0x9b, 0x70, 0x00, 0x2d, 0x82, 0xd9, 0x27, 0xa0, 0x70, 0x10, 0x0f, 0x17, 0x6b, + 0x46, 0xc7, 0x46, 0x8b, 0x6e, 0xee, 0x25, 0x2a, 0xb4, 0x29, 0xcb, 0x2d, 0x3b, 0x08, 0x35, 0xc3, + 0x9d, 0xa0, 0x8c, 0x52, 0x1c, 0xef, 0x0c, 0x0d, 0x23, 0xd4, 0x96, 0x75, 0x49, 0x3f, 0x17, 0xbb, + 0x64, 0xa2, 0x94, 0x6f, 0x10, 0x61, 0xa2, 0x55, 0x7f, 0x0a, 0xca, 0x7c, 0x36, 0x4b, 0xb3, 0xae, + 0x3f, 0x87, 0x95, 0x08, 0x13, 0xd6, 0xb7, 0xa0, 0x22, 0x95, 0xba, 0x42, 0x87, 0x87, 0xa4, 0x2c, + 0x24, 0x96, 0xca, 0x59, 0x11, 0xae, 0x0a, 0x49, 0xee, 0xea, 0xff, 0xbb, 0x0e, 0x0a, 0xf9, 0xe0, + 0x34, 0x0a, 0x33, 0xa0, 0x93, 0xba, 0x94, 0xf5, 0x97, 0x79, 0xa9, 0xf5, 0xb7, 0x03, 0x79, 0xc7, + 0x9e, 0xc6, 0x51, 0x5b, 0xa9, 0xf3, 0x23, 0x42, 0xe0, 0x58, 0x7b, 0xbe, 0x3d, 0x21, 0x3b, 0x55, + 0x8e, 0x60, 0x24, 0x37, 0x24, 0x9a, 0x7d, 0x34, 0x44, 0x0f, 0x00, 0x4c, 0x3b, 0x08, 0x75, 0xf2, + 0xdc, 0x88, 0x6a, 0x8b, 0x9e, 0x89, 0xfb, 0x5f, 0x2b, 0x9b, 0xf1, 0x50, 0xdc, 0x03, 0x45, 0xd2, + 0xf5, 0xdc, 0xd8, 0xe9, 0x92, 0xd7, 0x12, 0x1d, 0xb0, 0xe9, 0x36, 0xdd, 0x70, 0x89, 0x12, 0x35, + 0xa1, 0xc2, 0x12, 0x25, 0x6a, 0x41, 0x7f, 0xaa, 0x42, 0xae, 0xe7, 0x99, 0x16, 0x7b, 0x0f, 0xca, + 0x14, 0x16, 0x2c, 0x0d, 0xb0, 0xd0, 0xc5, 0x11, 0x4d, 0x7f, 0x68, 0xa4, 0x4a, 0xae, 0xf8, 0xf5, + 0xfc, 0x40, 0xe2, 0xd7, 0xc8, 0x86, 0xa4, 0xc3, 0x64, 0x6c, 0x52, 0x45, 0xf8, 0xb9, 0xc8, 0x2d, + 0xc3, 0x31, 0xb8, 0xb7, 0xd2, 0x69, 0x82, 0x6f, 0xb9, 0xa4, 0xf8, 0xe7, 0xb5, 0x38, 0x4d, 0x96, + 0xbb, 0xef, 0xa1, 0xea, 0xc5, 0x77, 0xa0, 0xfc, 0x0a, 0xcb, 0x9d, 0xe3, 0x69, 0x4b, 0x7a, 0x0f, + 0xca, 0x5f, 0x79, 0xb6, 0xcb, 0x2b, 0x5e, 0x58, 0xaa, 0x38, 0x1a, 0x46, 0xbc, 0xe2, 0x5f, 0x89, + 0x5f, 0xec, 0x36, 0x14, 0x3d, 0x97, 0xe7, 0x5d, 0x5c, 0xca, 0xbb, 0xe0, 0xb9, 0x5d, 0x1e, 0x17, + 0x56, 0xb3, 0x03, 0xdd, 0xb7, 0x27, 0x27, 0xa1, 0x8e, 0x9c, 0xe2, 0x10, 0xba, 0x62, 0x07, 0x1a, + 0xc2, 0x30, 0x5b, 0x9c, 0x78, 0x63, 0xdb, 0x41, 0x0d, 0x8f, 0x32, 0x2b, 0x2f, 0x65, 0x06, 0x1c, + 0x4d, 0x19, 0xbe, 0x0e, 0xa5, 0x89, 0xef, 0x91, 0x5a, 0x5b, 0x87, 0x25, 0xca, 0x22, 0xe1, 0x76, + 0x2f, 0x50, 0x7d, 0xa2, 0x9f, 0xb6, 0x3b, 0xd1, 0xc9, 0x19, 0x53, 0xd9, 0xc9, 0xde, 0x2b, 0x69, + 0xd5, 0x08, 0x48, 0x6e, 0x96, 0xd7, 0xa1, 0x64, 0x4c, 0x26, 0xba, 0x08, 0x6f, 0x5b, 0xca, 0xcb, + 0x98, 0x4c, 0xa8, 0xc8, 0x07, 0x50, 0x3b, 0xb3, 0x5d, 0x3d, 0x98, 0x59, 0x23, 0x4e, 0x5b, 0x5b, + 0xee, 0xca, 0x33, 0xdb, 0xc5, 0xd9, 0x4d, 0xf4, 0xf2, 0x32, 0xd8, 0xf8, 0xe6, 0xcb, 0x60, 0xf3, + 0x79, 0xcb, 0x40, 0x85, 0x82, 0x38, 0x69, 0x50, 0x96, 0x48, 0x04, 0x86, 0xbd, 0x0f, 0x15, 0xdf, + 0x70, 0x4f, 0x75, 0x71, 0xa2, 0xff, 0xa5, 0xec, 0x50, 0xd0, 0x0c, 0xf7, 0x54, 0x1c, 0xe8, 0x83, + 0x1f, 0xff, 0x4e, 0xab, 0xcc, 0x5b, 0x2f, 0x31, 0xc5, 0x24, 0x0b, 0x8f, 0xbd, 0xd8, 0xc2, 0xfb, + 0x90, 0x4c, 0x29, 0xcb, 0x0d, 0xf5, 0x88, 0xe1, 0xd2, 0x6a, 0x86, 0x2a, 0x27, 0xeb, 0x73, 0x36, + 0x6c, 0x00, 0x39, 0xf4, 0x74, 0xf2, 0xfe, 0x6d, 0xa7, 0x1a, 0x10, 0x7b, 0xfa, 0x34, 0xf0, 0x13, + 0xaf, 0x5f, 0x03, 0x36, 0x93, 0xa8, 0x62, 0x1e, 0xca, 0x7d, 0x59, 0x3e, 0x51, 0x48, 0x85, 0x21, + 0x47, 0xc6, 0x9b, 0x9d, 0x8a, 0x4d, 0xbe, 0x0d, 0x35, 0x1e, 0x42, 0xc3, 0xfb, 0x2d, 0x20, 0x25, + 0xa9, 0xac, 0x55, 0x09, 0xc8, 0xfb, 0x29, 0x20, 0x01, 0x23, 0x2c, 0xca, 0xf0, 0x9c, 0xb4, 0xa4, + 0x44, 0xc0, 0x70, 0x13, 0x32, 0x3c, 0xd7, 0xca, 0x66, 0xf4, 0x13, 0x37, 0xff, 0x63, 0xdb, 0x35, + 0x71, 0xea, 0x85, 0xc6, 0x24, 0xa8, 0xd7, 0x69, 0x65, 0x56, 0x04, 0x6c, 0x68, 0x4c, 0x02, 0xf6, + 0x01, 0x54, 0x0d, 0x6e, 0x6c, 0xf0, 0x7a, 0x5f, 0x93, 0x1d, 0x60, 0x92, 0x19, 0xa2, 0x55, 0x0c, + 0xc9, 0x26, 0xf9, 0x18, 0x58, 0x74, 0x10, 0x2a, 0xb9, 0x0b, 0xae, 0x2f, 0xcd, 0xc6, 0x4d, 0x71, + 0x12, 0x1a, 0x3b, 0x0b, 0x6e, 0x41, 0x85, 0x3b, 0x44, 0xf4, 0x20, 0xb4, 0x66, 0xf5, 0x57, 0xa8, + 0x42, 0xc0, 0x41, 0x83, 0xd0, 0x9a, 0xb1, 0x8f, 0xa1, 0x96, 0xb6, 0xb2, 0x6e, 0xac, 0x38, 0x4f, + 0xa4, 0x69, 0xa1, 0x55, 0x47, 0xb2, 0xdd, 0x75, 0x9b, 0x47, 0xc7, 0x93, 0x86, 0x44, 0x8c, 0xfc, + 0xcc, 0xac, 0xea, 0x7a, 0x61, 0x33, 0x82, 0x61, 0x07, 0x46, 0x76, 0x7c, 0x78, 0x4e, 0x4a, 0x55, + 0xdc, 0x81, 0xb1, 0xe9, 0x88, 0xc6, 0x51, 0x64, 0x45, 0xb6, 0x80, 0x87, 0x8c, 0xd0, 0x26, 0x6f, + 0xf9, 0x3c, 0x3c, 0x84, 0x74, 0xa8, 0xf8, 0x88, 0x71, 0x71, 0xef, 0xd1, 0x78, 0x28, 0x8d, 0xbc, + 0x1b, 0x7d, 0x02, 0x1b, 0x33, 0xdf, 0xd2, 0xa5, 0x92, 0x55, 0xb9, 0x51, 0x87, 0xbe, 0x95, 0x14, + 0x5e, 0x9d, 0x49, 0x29, 0xf6, 0x03, 0xd8, 0x92, 0x38, 0xe7, 0xa7, 0xc4, 0x7c, 0x9b, 0x98, 0xb7, + 0x17, 0x98, 0x8f, 0x4e, 0x91, 0x7d, 0x63, 0x96, 0x4a, 0xb3, 0x6d, 0xc8, 0x77, 0x82, 0xb6, 0x6b, + 0x92, 0x6e, 0x55, 0xd2, 0x78, 0x82, 0x3d, 0x82, 0x2a, 0x37, 0x64, 0xc8, 0x8a, 0x0e, 0xea, 0x77, + 0x65, 0x0f, 0x37, 0x59, 0x33, 0xdc, 0x14, 0xaf, 0x38, 0xf1, 0xef, 0x80, 0x7d, 0x04, 0x5b, 0xfc, + 0xf8, 0x41, 0x16, 0x91, 0x6f, 0x2c, 0x0f, 0x39, 0x11, 0xed, 0x25, 0x72, 0x52, 0x83, 0x6b, 0xfe, + 0xdc, 0x25, 0xe3, 0x46, 0x70, 0x72, 0xc7, 0x18, 0xf1, 0xdf, 0x23, 0xfe, 0xab, 0x62, 0x75, 0x71, + 0x32, 0xce, 0x4b, 0xb2, 0xe9, 0x8a, 0x2f, 0x83, 0x0e, 0x91, 0xef, 0x39, 0x79, 0x72, 0x87, 0x11, + 0xe5, 0xf9, 0xe6, 0xb7, 0xc9, 0x93, 0x9c, 0x49, 0x94, 0x27, 0x83, 0xdc, 0x7c, 0x6e, 0x9b, 0xa4, + 0xe9, 0x55, 0x35, 0xfa, 0xcd, 0x5e, 0x87, 0x0d, 0xdf, 0x1a, 0xcd, 0xfd, 0xc0, 0x7e, 0x66, 0xe9, + 0x81, 0xed, 0x9e, 0x92, 0x8e, 0x57, 0xd2, 0x6a, 0x31, 0x74, 0x60, 0xbb, 0xa7, 0x28, 0x32, 0xac, + 0xf3, 0xd0, 0xf2, 0x5d, 0x7e, 0xad, 0xe0, 0x6d, 0x59, 0x64, 0xb4, 0x09, 0x81, 0xeb, 0x5c, 0x03, + 0x2b, 0xfe, 0xbd, 0x30, 0xb2, 0x01, 0x1f, 0xd9, 0x07, 0xdf, 0x68, 0x64, 0x07, 0x34, 0xb2, 0x77, + 0xa1, 0x64, 0xbb, 0xa1, 0xe5, 0x3f, 0x33, 0x9c, 0xfa, 0xbb, 0x4b, 0xd2, 0x38, 0xc6, 0xb1, 0x3b, + 0x50, 0x0c, 0x1c, 0x1b, 0xd7, 0x7b, 0xfd, 0xbd, 0x25, 0xb2, 0x08, 0xc5, 0xee, 0x41, 0x39, 0xbe, + 0x3d, 0x57, 0x7f, 0x7f, 0x89, 0x2e, 0x41, 0xb2, 0x9b, 0x90, 0x3b, 0xc3, 0x09, 0xf5, 0x70, 0xf9, + 0x44, 0x02, 0xe1, 0xb8, 0x7d, 0x8f, 0x51, 0x67, 0xa7, 0xed, 0xfb, 0xd1, 0xd2, 0xf6, 0xbd, 0x67, + 0x3b, 0x0e, 0xdf, 0xbe, 0xc7, 0xe2, 0x17, 0x6e, 0x7e, 0xc4, 0x81, 0x2d, 0xf9, 0x60, 0x79, 0xf3, + 0x43, 0xdc, 0x13, 0xba, 0x67, 0x58, 0x09, 0xc8, 0xcd, 0xce, 0x4f, 0x0b, 0x3e, 0x94, 0xfb, 0x2a, + 0xed, 0x7f, 0xd7, 0x20, 0x88, 0xd3, 0x68, 0x80, 0x88, 0x43, 0x06, 0x54, 0x9a, 0x3e, 0xe2, 0x97, + 0x52, 0x38, 0xa4, 0x63, 0x9e, 0xb3, 0xf7, 0xa0, 0x16, 0x45, 0x2f, 0x61, 0x71, 0x41, 0xfd, 0xe3, + 0xa5, 0x1a, 0xa4, 0x09, 0x58, 0x0b, 0xaa, 0x63, 0xd4, 0xdd, 0xa7, 0x5c, 0x95, 0xaf, 0x7f, 0x42, + 0x15, 0xd9, 0x89, 0x36, 0xd6, 0xe7, 0xa9, 0xfa, 0x5a, 0x8a, 0x8b, 0x3d, 0x00, 0x66, 0x8f, 0xf9, + 0x78, 0xee, 0xf9, 0xde, 0x94, 0xab, 0xeb, 0xf5, 0x4f, 0xb9, 0x43, 0x6c, 0x19, 0x43, 0x67, 0x8e, + 0x96, 0x6b, 0xea, 0xd3, 0x40, 0xa8, 0x09, 0xdf, 0xa3, 0x7a, 0x0a, 0xe1, 0x15, 0xdf, 0xb2, 0x8d, + 0x7c, 0xca, 0x48, 0x7b, 0x10, 0x70, 0xad, 0xe1, 0x53, 0xc0, 0xe9, 0xfa, 0x2c, 0x61, 0xfd, 0xb5, + 0x17, 0xb2, 0x22, 0x6d, 0xc4, 0xfa, 0x09, 0x6c, 0x70, 0xd7, 0x1d, 0x69, 0x64, 0x38, 0x45, 0xbf, + 0x2f, 0x4b, 0x2e, 0xd9, 0x53, 0xad, 0x55, 0x4d, 0xd9, 0x6f, 0xfd, 0x31, 0x6c, 0x4a, 0x9e, 0x3b, + 0x2a, 0xf6, 0xd7, 0xe5, 0x62, 0x63, 0xd7, 0x9d, 0x56, 0x9b, 0x47, 0x3f, 0xa9, 0xc8, 0x47, 0x50, + 0xa3, 0x5d, 0x34, 0xbe, 0xc6, 0xf4, 0x1b, 0x2b, 0xaf, 0x31, 0x55, 0x91, 0x28, 0x4a, 0xa1, 0xf0, + 0x4f, 0xd6, 0xe9, 0x28, 0xb4, 0xea, 0x3f, 0xe0, 0xc2, 0x3f, 0x06, 0x36, 0x43, 0x8b, 0x3d, 0x02, + 0x30, 0x66, 0x33, 0xe7, 0x82, 0x4f, 0xcd, 0x1f, 0xd2, 0xd4, 0xdc, 0x96, 0xa6, 0x66, 0x03, 0x91, + 0x34, 0x37, 0xcb, 0x46, 0xf4, 0x93, 0x3d, 0x84, 0xea, 0xcc, 0x0b, 0x42, 0xdd, 0x9c, 0x3a, 0xd4, + 0xfe, 0x86, 0xbc, 0xb6, 0x0f, 0xbd, 0x20, 0x6c, 0x4d, 0x1d, 0x6c, 0x05, 0xcc, 0xe2, 0xdf, 0xac, + 0x0b, 0x97, 0x3c, 0x57, 0x37, 0xe7, 0x33, 0xc7, 0x1e, 0x61, 0x0f, 0x18, 0x74, 0xc2, 0x5f, 0xdf, + 0xa5, 0x12, 0x6f, 0x48, 0x25, 0xf6, 0xdd, 0x56, 0x44, 0x24, 0x42, 0xe9, 0xb6, 0xbc, 0x45, 0x10, + 0xd9, 0x99, 0x34, 0x06, 0x71, 0xe8, 0x69, 0x93, 0xab, 0x06, 0x04, 0x8d, 0x62, 0x4f, 0x3f, 0x81, + 0xcd, 0x84, 0x0a, 0x1b, 0x18, 0xd4, 0x5b, 0xf2, 0x4c, 0x96, 0x62, 0xd9, 0x6b, 0x11, 0x23, 0xc2, + 0x02, 0xea, 0x3b, 0xcf, 0x71, 0xe6, 0x33, 0x21, 0x4a, 0xeb, 0x6d, 0xd1, 0x77, 0x04, 0xe4, 0x52, + 0x52, 0x32, 0xc5, 0xad, 0x69, 0x7d, 0x4f, 0x36, 0xc5, 0xad, 0x29, 0xaa, 0x19, 0x22, 0xae, 0xf8, + 0x99, 0x6d, 0x9d, 0x05, 0xf5, 0xc7, 0x14, 0x12, 0x29, 0x22, 0xb7, 0x9f, 0x20, 0x08, 0xf7, 0x7d, + 0xd3, 0xf6, 0xd1, 0x04, 0xa0, 0x18, 0xb0, 0x7d, 0x1e, 0xfa, 0xcb, 0x41, 0x48, 0xc1, 0x9a, 0x50, + 0x27, 0x71, 0x2e, 0xd6, 0x6a, 0xea, 0xa8, 0xaf, 0xb3, 0xb4, 0x24, 0x2f, 0x23, 0x2d, 0x3f, 0x49, + 0x3c, 0x94, 0x0e, 0xfd, 0x7e, 0x08, 0xaf, 0xae, 0xce, 0x24, 0xba, 0xe5, 0xf9, 0x19, 0xe9, 0x1b, + 0xd7, 0x56, 0x70, 0x37, 0xe9, 0xe2, 0xa7, 0xfa, 0xb3, 0x3c, 0x94, 0x22, 0xd3, 0x88, 0x55, 0xa0, + 0x78, 0xd4, 0xfb, 0xbc, 0xd7, 0x7f, 0xda, 0xe3, 0x97, 0x1d, 0x1b, 0x83, 0x41, 0x5b, 0x1b, 0x2a, + 0x26, 0xdb, 0x00, 0xa0, 0x0b, 0x44, 0xfa, 0xa0, 0xd9, 0xe8, 0xf1, 0xcb, 0x8f, 0x74, 0x6d, 0x89, + 0xa7, 0xd7, 0xd9, 0x16, 0xd4, 0xf6, 0x8e, 0x7a, 0x14, 0xbe, 0xc8, 0x41, 0x59, 0x04, 0xb5, 0xbf, + 0xe0, 0x07, 0xa9, 0x1c, 0x94, 0x43, 0xd0, 0x41, 0x63, 0xd8, 0xd6, 0x3a, 0x11, 0x28, 0x4f, 0x91, + 0x90, 0xfd, 0x23, 0xad, 0x29, 0x72, 0x2a, 0xb0, 0xcb, 0xb0, 0x15, 0xb3, 0x45, 0x59, 0x2a, 0x45, + 0xac, 0xd9, 0xa1, 0xd6, 0xff, 0xac, 0xdd, 0x1c, 0x2a, 0x40, 0xa7, 0xb2, 0x8f, 0x1f, 0x2b, 0x15, + 0x56, 0x85, 0x52, 0xab, 0x33, 0x18, 0x76, 0x7a, 0xcd, 0xa1, 0x52, 0xc5, 0x0a, 0xef, 0x75, 0xba, + 0xc3, 0xb6, 0xa6, 0xd4, 0x58, 0x09, 0x72, 0x9f, 0xf5, 0x3b, 0x3d, 0x65, 0x83, 0x2e, 0x52, 0x35, + 0x0e, 0x0e, 0xbb, 0x6d, 0x65, 0x13, 0xa1, 0x83, 0xbe, 0x36, 0x54, 0x14, 0x84, 0x3e, 0xed, 0xf4, + 0x5a, 0xfd, 0xa7, 0xca, 0x16, 0x2b, 0x43, 0xfe, 0xa8, 0x87, 0xc5, 0x30, 0x56, 0x83, 0x32, 0xfd, + 0xd4, 0x1b, 0xdd, 0xae, 0x72, 0x49, 0x3a, 0xca, 0xdd, 0x46, 0x14, 0x1d, 0x0c, 0x0f, 0xb0, 0x0e, + 0x97, 0xb1, 0x2d, 0x71, 0x92, 0xa8, 0xaf, 0x60, 0x3e, 0x07, 0x9d, 0xde, 0xd1, 0x40, 0xb9, 0x8a, + 0xc4, 0xf4, 0x93, 0x30, 0x75, 0xcc, 0xa7, 0xd3, 0xa3, 0xae, 0xbc, 0x89, 0xbf, 0x5b, 0xed, 0x6e, + 0x7b, 0xd8, 0x56, 0x6e, 0x61, 0xab, 0xba, 0xfd, 0xe6, 0xe7, 0x7a, 0xff, 0x50, 0x79, 0x0d, 0xfb, + 0xf4, 0x50, 0x6b, 0xeb, 0x82, 0xf0, 0x36, 0x53, 0xa0, 0xba, 0x77, 0xf4, 0xe3, 0x1f, 0x7f, 0xa9, + 0x8b, 0x46, 0xbd, 0x8e, 0x65, 0x26, 0x14, 0xfa, 0xd1, 0xe7, 0xca, 0xdd, 0x05, 0xd0, 0xe0, 0x73, + 0xe5, 0x0d, 0xec, 0x94, 0xa8, 0x97, 0x95, 0x7b, 0x48, 0xa0, 0xb5, 0x9b, 0x47, 0xda, 0xa0, 0xf3, + 0xa4, 0xad, 0x37, 0x87, 0x6d, 0xe5, 0x4d, 0xea, 0x85, 0x4e, 0xef, 0x73, 0xe5, 0x3e, 0x56, 0x13, + 0x7f, 0xf1, 0xbe, 0x7f, 0x8b, 0x31, 0xd8, 0x48, 0x68, 0x09, 0xf6, 0x36, 0x92, 0xec, 0x6a, 0xfd, + 0x46, 0xab, 0xd9, 0x18, 0x0c, 0x95, 0x77, 0xb0, 0x8d, 0x83, 0xc3, 0x6e, 0x67, 0xa8, 0x3c, 0xc0, + 0x86, 0x3c, 0x6e, 0x0c, 0xf7, 0xdb, 0x9a, 0xf2, 0x2e, 0x0e, 0xe3, 0xb0, 0x73, 0xd0, 0xd6, 0x45, + 0x9f, 0x3e, 0xc4, 0x32, 0xf6, 0x3a, 0xdd, 0xae, 0xf2, 0x88, 0x8e, 0x22, 0x1b, 0xda, 0xb0, 0x43, + 0x03, 0xf9, 0x01, 0x66, 0xd0, 0x38, 0x3c, 0xec, 0x7e, 0xa9, 0x7c, 0x88, 0x0d, 0x3c, 0x38, 0xea, + 0x0e, 0x3b, 0xfa, 0xd1, 0x61, 0xab, 0x31, 0x6c, 0x2b, 0x1f, 0xd1, 0x28, 0xf7, 0x07, 0xc3, 0xd6, + 0x41, 0x57, 0xf9, 0x98, 0xf2, 0xa4, 0x39, 0xd6, 0xec, 0xf6, 0x7b, 0x6d, 0xe5, 0x13, 0x35, 0x57, + 0xda, 0x51, 0x76, 0xd4, 0x5c, 0x49, 0x55, 0x54, 0xf5, 0xb7, 0xa1, 0x14, 0xd9, 0xc5, 0x98, 0x65, + 0xa7, 0xd7, 0x6b, 0x6b, 0xca, 0x1a, 0x16, 0xdb, 0x6d, 0xef, 0x0d, 0x95, 0x0c, 0x9d, 0xd1, 0x76, + 0x1e, 0xef, 0x0f, 0x95, 0x75, 0xfc, 0xd9, 0x3f, 0xc2, 0x1e, 0xcc, 0x52, 0xd3, 0xdb, 0x07, 0x1d, + 0x25, 0x87, 0xbf, 0x1a, 0xbd, 0x61, 0x47, 0xc9, 0xd3, 0x04, 0xe9, 0xf4, 0x1e, 0x77, 0xdb, 0x4a, + 0x01, 0xa1, 0x07, 0x0d, 0xed, 0x73, 0xa5, 0xc8, 0x33, 0x6d, 0xb5, 0xbf, 0x50, 0x4a, 0xac, 0x00, + 0xeb, 0xdd, 0x87, 0x4a, 0x19, 0x41, 0xad, 0x76, 0xeb, 0xe8, 0x50, 0x01, 0xf5, 0x1e, 0x14, 0x1b, + 0x93, 0xc9, 0x81, 0x67, 0xd2, 0xb1, 0xf0, 0xde, 0x51, 0xb7, 0xcb, 0x17, 0xcc, 0x6e, 0x7f, 0x38, + 0xec, 0x1f, 0x28, 0x19, 0x9c, 0xa2, 0xc3, 0xfe, 0xa1, 0xb2, 0xae, 0x76, 0xa0, 0x14, 0x69, 0x00, + 0xd2, 0x2d, 0xc0, 0x12, 0xe4, 0x0e, 0xb5, 0xf6, 0x13, 0x1e, 0x58, 0xd0, 0x6b, 0x7f, 0x81, 0xd5, + 0xc4, 0x5f, 0x98, 0x51, 0x16, 0x0b, 0xe2, 0xd7, 0xf5, 0xe8, 0x1a, 0x60, 0xb7, 0xd3, 0x6b, 0x37, + 0x34, 0x25, 0xaf, 0x7e, 0x08, 0x5b, 0x4b, 0xf2, 0x93, 0x8a, 0x6f, 0x74, 0x44, 0xf1, 0x9d, 0xc7, + 0xbd, 0xbe, 0xd6, 0xe6, 0xf7, 0x0a, 0x45, 0xa7, 0xae, 0xab, 0x6f, 0x41, 0x39, 0x16, 0xf4, 0x38, + 0xc9, 0x9a, 0x5a, 0x7f, 0x30, 0xe0, 0x63, 0xb0, 0x86, 0x69, 0xea, 0x1b, 0x9e, 0xce, 0x7c, 0x96, + 0x2b, 0xdd, 0x52, 0x76, 0x3e, 0xcb, 0x95, 0xee, 0x28, 0xaf, 0xab, 0x03, 0xd8, 0x8a, 0xf6, 0x1b, + 0xba, 0xd3, 0x40, 0x86, 0xd0, 0x36, 0xe4, 0xbb, 0xd6, 0x33, 0xcb, 0x89, 0x82, 0xf3, 0x29, 0x81, + 0xd0, 0xfe, 0xf1, 0x57, 0x9d, 0xf8, 0x52, 0x3a, 0x25, 0x50, 0xc1, 0xec, 0x49, 0xf7, 0xe2, 0xe9, + 0x2a, 0xe7, 0xff, 0x9f, 0x81, 0x52, 0xbc, 0x8b, 0xdd, 0x81, 0xf5, 0xe1, 0x40, 0x1c, 0x59, 0x6d, + 0x3f, 0x48, 0x9e, 0x01, 0x19, 0x46, 0xbf, 0xb4, 0xf5, 0xe1, 0x80, 0xbd, 0x0d, 0x05, 0x7e, 0x35, + 0x57, 0xb8, 0xab, 0xb6, 0xd3, 0x3b, 0xe3, 0x90, 0x70, 0x9a, 0xa0, 0x61, 0x1f, 0x42, 0x39, 0xae, + 0xad, 0xf0, 0xdf, 0x5c, 0x4d, 0x33, 0xc4, 0x68, 0x2d, 0xa1, 0x54, 0xbb, 0xb0, 0x91, 0xce, 0x90, + 0xdd, 0x04, 0xe0, 0x59, 0x4a, 0x4e, 0x4a, 0x09, 0xc2, 0xae, 0x43, 0x74, 0x63, 0xb8, 0x45, 0x15, + 0xab, 0xc5, 0x37, 0x88, 0x5b, 0xea, 0xbf, 0xc8, 0x02, 0x24, 0x7a, 0x30, 0x76, 0x44, 0xec, 0x95, + 0xca, 0x8b, 0xa3, 0xff, 0x57, 0xa0, 0xec, 0x78, 0x86, 0x29, 0xbf, 0x02, 0x52, 0x42, 0x00, 0x0d, + 0x93, 0x7c, 0xb7, 0xad, 0xcc, 0xe3, 0x6e, 0xd8, 0x15, 0x28, 0x8c, 0x3d, 0x7f, 0x6a, 0x84, 0xe2, + 0x26, 0x86, 0x48, 0xe1, 0x76, 0x66, 0x4f, 0x5c, 0xcf, 0xb7, 0xd0, 0x1a, 0x70, 0xe9, 0x32, 0x06, + 0x8e, 0x41, 0x55, 0x00, 0xbb, 0x08, 0xc3, 0xed, 0xc8, 0x72, 0x47, 0x8e, 0x17, 0x58, 0x26, 0x6e, + 0x30, 0x05, 0x52, 0xf9, 0x21, 0x02, 0xed, 0x5e, 0xf0, 0xd6, 0xfa, 0x53, 0xdb, 0x35, 0x42, 0x71, + 0x4c, 0x45, 0xad, 0x8d, 0x20, 0x58, 0xdd, 0xaf, 0x02, 0x4f, 0x38, 0xa9, 0xf8, 0x45, 0xae, 0x12, + 0x02, 0xa8, 0xba, 0xaf, 0x02, 0x58, 0xc1, 0xc8, 0x98, 0xf1, 0xcc, 0xcb, 0xfc, 0x36, 0xad, 0x80, + 0xec, 0x5e, 0xb0, 0x2e, 0x6c, 0x0c, 0x8f, 0x71, 0xfb, 0xf5, 0x5a, 0x46, 0x68, 0x34, 0x3d, 0x47, + 0xb8, 0x8f, 0xee, 0x2c, 0x1a, 0x0c, 0x0f, 0xd2, 0x64, 0x3c, 0x00, 0x75, 0x81, 0x17, 0x0d, 0x78, + 0x7b, 0x64, 0x1d, 0x5b, 0xfe, 0x84, 0x1b, 0x1f, 0x95, 0xd4, 0x15, 0x0a, 0x8e, 0x21, 0xeb, 0xa3, + 0x62, 0x27, 0x89, 0xeb, 0x0d, 0xb8, 0xb4, 0x22, 0xf3, 0x6f, 0x15, 0xc1, 0xf8, 0x4f, 0xd6, 0xa1, + 0x22, 0xe5, 0x4f, 0xe7, 0x04, 0xc6, 0x8c, 0xfc, 0x5c, 0x71, 0x08, 0x77, 0x59, 0x40, 0xf8, 0x85, + 0x95, 0x91, 0x11, 0x1a, 0x8e, 0x37, 0x91, 0xae, 0xe2, 0x08, 0x48, 0xc7, 0xa4, 0xa3, 0x3b, 0x63, + 0x6a, 0x05, 0x33, 0x63, 0x14, 0xad, 0x90, 0x04, 0x90, 0x3c, 0x3f, 0x93, 0x93, 0x9f, 0x9f, 0x51, + 0xf8, 0x19, 0x2f, 0x8f, 0xc5, 0xa1, 0x23, 0xdd, 0x5b, 0x50, 0x89, 0xf4, 0x46, 0x2c, 0x45, 0x1c, + 0x43, 0x46, 0xa0, 0x8e, 0xc9, 0xee, 0x42, 0xf2, 0xbc, 0x8e, 0x6e, 0x04, 0xba, 0x37, 0x8e, 0x02, + 0x01, 0x63, 0x70, 0x23, 0xe8, 0x8f, 0xe3, 0xb3, 0x81, 0xa9, 0x67, 0xc6, 0xe3, 0x8b, 0x00, 0x92, + 0x71, 0x0f, 0xe0, 0x92, 0x70, 0x58, 0x5a, 0xa6, 0x3e, 0xb6, 0x2d, 0xc7, 0xd4, 0x6d, 0x93, 0x1f, + 0xd4, 0xe7, 0xb5, 0xad, 0x18, 0xb5, 0x87, 0x98, 0x8e, 0x49, 0x1a, 0x96, 0xb0, 0x52, 0x4d, 0x7b, + 0x62, 0x05, 0xa1, 0x08, 0x5b, 0xab, 0x72, 0x60, 0x8b, 0x60, 0xaa, 0x13, 0x2d, 0x91, 0x46, 0x18, + 0xd2, 0x65, 0xb9, 0x58, 0xdd, 0xcb, 0x44, 0xf7, 0x68, 0xb8, 0xa6, 0xf7, 0x0a, 0x85, 0x7c, 0x89, + 0xb8, 0x65, 0xb1, 0x52, 0xe2, 0x78, 0xe4, 0xbb, 0xb0, 0x89, 0x48, 0x51, 0x29, 0x22, 0xe1, 0x17, + 0xa8, 0x6a, 0x23, 0xcf, 0xe1, 0x15, 0x42, 0xa0, 0xfa, 0x97, 0xf3, 0x00, 0x89, 0xa1, 0x4f, 0x77, + 0xf3, 0xc8, 0x03, 0x17, 0x8f, 0x5c, 0x91, 0xd2, 0x1d, 0x93, 0x3d, 0x84, 0x2b, 0xe2, 0x26, 0x60, + 0x1c, 0x4b, 0x62, 0xbb, 0xfa, 0xb1, 0x11, 0xc5, 0xae, 0x31, 0x81, 0xe5, 0x51, 0x02, 0x1d, 0x77, + 0xd7, 0x40, 0xb3, 0x61, 0x53, 0xe6, 0x09, 0x2f, 0x66, 0x69, 0x8f, 0xbf, 0xac, 0x8c, 0x26, 0xec, + 0xc3, 0x8b, 0x19, 0x7b, 0x0f, 0x2e, 0xfb, 0xd6, 0xd8, 0xb7, 0x82, 0x13, 0x3d, 0x0c, 0xe4, 0xc2, + 0x78, 0x70, 0xe9, 0x96, 0x40, 0x0e, 0x83, 0xb8, 0xac, 0xf7, 0xe0, 0xb2, 0xe8, 0xdc, 0x85, 0xea, + 0x71, 0x4f, 0xfa, 0x16, 0x47, 0xca, 0xb5, 0xa3, 0x88, 0x63, 0xf2, 0x7e, 0x44, 0x0f, 0x1a, 0x95, + 0xb4, 0x32, 0xf7, 0x74, 0x88, 0xab, 0xfb, 0xe4, 0xc2, 0xa0, 0x85, 0x5b, 0xd2, 0x78, 0x82, 0xa9, + 0x90, 0xc3, 0xb1, 0xa7, 0xa1, 0xdb, 0x78, 0xb8, 0xf1, 0x80, 0x1e, 0x6c, 0xa2, 0x67, 0x17, 0x3c, + 0xd3, 0xd2, 0x08, 0xc7, 0xde, 0xc1, 0x79, 0x91, 0x34, 0x3b, 0x7a, 0x73, 0x84, 0x87, 0x53, 0x28, + 0x49, 0x43, 0x35, 0xfe, 0xfa, 0xc8, 0x5b, 0xc0, 0xa4, 0x9a, 0x47, 0xd4, 0x55, 0xa2, 0xde, 0x8c, + 0xab, 0x2d, 0x88, 0xdf, 0x00, 0xaa, 0x22, 0x3f, 0x46, 0xab, 0x2d, 0xdb, 0xfb, 0x88, 0xa4, 0x13, + 0xb5, 0xf7, 0xe0, 0x72, 0xd2, 0x3a, 0xdd, 0x08, 0xf5, 0xf0, 0xc4, 0xd2, 0x2d, 0xd7, 0xa4, 0xc8, + 0x89, 0x92, 0xb6, 0x15, 0x37, 0xb4, 0x11, 0x0e, 0x4f, 0x2c, 0xb4, 0xd8, 0x25, 0x8f, 0xea, 0xe6, + 0x8b, 0x3d, 0xaa, 0x1f, 0x41, 0x3d, 0xad, 0x53, 0x4b, 0xdd, 0xcd, 0xaf, 0x3f, 0x6f, 0xcb, 0x21, + 0x29, 0x71, 0x8f, 0xdf, 0x87, 0xad, 0x13, 0x23, 0x48, 0xeb, 0xe3, 0xe4, 0xe8, 0x2d, 0x69, 0x9b, + 0x27, 0x46, 0x20, 0x2b, 0xe1, 0xea, 0x1f, 0x64, 0x60, 0x23, 0xed, 0xfa, 0xe0, 0x77, 0xca, 0xf8, + 0x6d, 0xab, 0x0c, 0xad, 0xb1, 0x28, 0x89, 0x6b, 0x81, 0xc7, 0xad, 0xcc, 0xa7, 0x6e, 0xb4, 0x16, + 0x66, 0xa7, 0xfc, 0x0e, 0x16, 0x7b, 0x13, 0x8a, 0xb3, 0x53, 0x2e, 0xa1, 0x9f, 0x37, 0xfb, 0x0a, + 0x33, 0x1e, 0x6f, 0xff, 0x26, 0x14, 0xe7, 0xa7, 0xc9, 0xc3, 0x06, 0x2b, 0x49, 0xe7, 0x44, 0xaa, + 0xfe, 0xd3, 0x75, 0xa8, 0xca, 0x1e, 0xbb, 0x6f, 0x12, 0x69, 0xf2, 0xad, 0x62, 0x8d, 0x76, 0x28, + 0x24, 0x56, 0xa7, 0x80, 0x76, 0xec, 0x27, 0x1e, 0x66, 0x02, 0x27, 0x46, 0xd0, 0x98, 0x87, 0x1e, + 0x8a, 0x7c, 0x14, 0xa5, 0x9e, 0x13, 0x05, 0xfd, 0xf3, 0x95, 0x81, 0x32, 0x41, 0xc4, 0xfb, 0xbf, + 0x27, 0xee, 0x14, 0xd1, 0x85, 0x43, 0x8a, 0x1e, 0xcd, 0x2f, 0xcd, 0x97, 0x6a, 0x74, 0xdf, 0x90, + 0xa2, 0x65, 0x1f, 0xc2, 0x66, 0x72, 0x83, 0x83, 0xb3, 0x14, 0x96, 0x58, 0x6a, 0xf1, 0xf5, 0x0d, + 0xf1, 0x18, 0x42, 0xcd, 0x0e, 0x74, 0xcf, 0x31, 0xa3, 0xab, 0x62, 0xc5, 0xe8, 0x3c, 0xa5, 0xef, + 0x98, 0xe2, 0x96, 0x2b, 0xa7, 0x71, 0xad, 0xb3, 0x88, 0x26, 0x3e, 0x73, 0xe9, 0x59, 0x67, 0xe2, + 0xca, 0xd8, 0x9f, 0x65, 0x60, 0x6b, 0xc9, 0x49, 0x87, 0xa2, 0x3d, 0x79, 0x28, 0x0c, 0x7f, 0xa2, + 0x91, 0x39, 0x35, 0xc2, 0xd1, 0x89, 0x3e, 0xf3, 0xad, 0xb1, 0x7d, 0x1e, 0xbd, 0x76, 0x46, 0xb0, + 0x43, 0x02, 0xa1, 0xf4, 0xe7, 0xe7, 0x86, 0xfc, 0x18, 0x83, 0x0b, 0x3e, 0x7e, 0x56, 0xd8, 0xa5, + 0xf3, 0x8b, 0x28, 0x9a, 0x36, 0xf7, 0x9c, 0x68, 0xda, 0xeb, 0x50, 0x76, 0x3d, 0x8a, 0x9c, 0x9a, + 0x9d, 0x8a, 0x90, 0xb3, 0xa2, 0xeb, 0x85, 0x7d, 0xf7, 0xf0, 0x94, 0x3d, 0x84, 0xcb, 0xf3, 0x80, + 0xe2, 0x06, 0x8e, 0x2d, 0x3f, 0x38, 0xb1, 0x63, 0x73, 0x99, 0x0b, 0x90, 0x4b, 0xf3, 0xc0, 0x3a, + 0x88, 0x71, 0xbc, 0x25, 0xea, 0x0d, 0x28, 0x74, 0x62, 0xe7, 0x62, 0x1c, 0xbe, 0x97, 0x15, 0x4f, + 0x04, 0x79, 0x50, 0xe6, 0x56, 0xe7, 0x81, 0x31, 0x63, 0xf7, 0x21, 0x3b, 0x35, 0x66, 0xe2, 0xb0, + 0xb2, 0x1e, 0x9f, 0xe8, 0x72, 0xec, 0x83, 0x03, 0x63, 0xc6, 0x77, 0x7f, 0x24, 0xba, 0xfe, 0x11, + 0x94, 0x22, 0xc0, 0xb7, 0xda, 0xb1, 0xff, 0xf5, 0x3a, 0x94, 0x5b, 0xf2, 0xe1, 0xc0, 0xc8, 0x70, + 0xf5, 0xd0, 0x9f, 0xbb, 0xa8, 0x40, 0x47, 0x8f, 0xa1, 0x8c, 0x0c, 0x77, 0x28, 0x40, 0xd1, 0xd4, + 0x5e, 0x7f, 0xc1, 0xd4, 0xbe, 0x01, 0xc0, 0x83, 0xb9, 0xc8, 0xbd, 0x96, 0x8d, 0xc3, 0x99, 0x3b, + 0x66, 0xc7, 0x3c, 0x5f, 0x1d, 0x62, 0x95, 0xfb, 0xe6, 0x21, 0x56, 0xf9, 0x95, 0x21, 0x56, 0x77, + 0x93, 0xed, 0x05, 0xa7, 0x38, 0x16, 0x5c, 0xe6, 0x9b, 0xdc, 0x2c, 0xbe, 0x41, 0x85, 0xa5, 0x7f, + 0x0f, 0x36, 0xa2, 0xd6, 0x89, 0xfc, 0x20, 0x75, 0x69, 0x4b, 0xe0, 0xf8, 0x69, 0x42, 0x2d, 0x94, + 0x93, 0xe9, 0x25, 0x5b, 0x79, 0x49, 0x18, 0xd7, 0x5f, 0xc9, 0x00, 0x13, 0xbe, 0xa0, 0xbd, 0xb9, + 0xe3, 0x0c, 0xad, 0x73, 0x92, 0x0c, 0xf7, 0x61, 0x4b, 0x1c, 0x76, 0x48, 0xf7, 0x6a, 0xc5, 0xf1, + 0x3e, 0x47, 0x24, 0xc7, 0xfb, 0xab, 0xae, 0xe0, 0xae, 0xaf, 0xbc, 0x82, 0xbb, 0xfa, 0x6a, 0xef, + 0x2d, 0xa8, 0xc8, 0x17, 0x58, 0xb9, 0x9e, 0x04, 0x46, 0x7c, 0x77, 0x55, 0xfd, 0xb3, 0x75, 0x80, + 0xc4, 0x5f, 0xf5, 0xab, 0x8e, 0x8f, 0x5b, 0x31, 0x24, 0xd9, 0x55, 0x43, 0x72, 0x0f, 0x14, 0x99, + 0x4e, 0xba, 0x49, 0xbd, 0x91, 0x10, 0x46, 0x6a, 0x8e, 0x1d, 0xc8, 0x57, 0x58, 0xe9, 0x96, 0x8b, + 0x08, 0x29, 0xe2, 0x48, 0xee, 0x3c, 0x17, 0x0b, 0xb0, 0x64, 0x07, 0x5c, 0x26, 0xb3, 0x4f, 0xe1, + 0x5a, 0xcc, 0xa9, 0x9f, 0xd9, 0xe1, 0x89, 0x37, 0x0f, 0xc5, 0x62, 0x0d, 0x84, 0x94, 0xba, 0x12, + 0xe5, 0xf4, 0x94, 0xa3, 0xf9, 0x7a, 0x0d, 0xd0, 0x5a, 0x1a, 0xcf, 0x1d, 0x47, 0x0f, 0xad, 0xf3, + 0x50, 0xbc, 0x7e, 0x52, 0x4f, 0xb9, 0xfa, 0xa4, 0xe1, 0xd5, 0x4a, 0x63, 0x91, 0x50, 0xff, 0x7e, + 0x0e, 0xf2, 0x3f, 0x9a, 0x5b, 0xfe, 0x05, 0xfb, 0x08, 0xca, 0x41, 0x38, 0x0d, 0xe5, 0x53, 0x77, + 0xf1, 0x46, 0x0e, 0xe1, 0xe9, 0xd0, 0xdc, 0x9a, 0x5a, 0x6e, 0xc8, 0x7d, 0xe0, 0x48, 0x4b, 0x1b, + 0xd0, 0x36, 0xe4, 0x83, 0xd0, 0x9a, 0x05, 0x22, 0x44, 0x97, 0x27, 0xd8, 0x0e, 0xe4, 0x5d, 0xcf, + 0xb4, 0x82, 0x74, 0x20, 0x6e, 0x0f, 0x35, 0x0e, 0x8e, 0x60, 0x2a, 0x14, 0xe2, 0x11, 0x5f, 0x3a, + 0xf9, 0xe6, 0x18, 0xba, 0xde, 0x63, 0x19, 0xa6, 0xed, 0x4e, 0xa2, 0x9b, 0xe9, 0x71, 0x1a, 0xb7, + 0x56, 0xb2, 0xb2, 0x8c, 0x49, 0xf4, 0xa2, 0x85, 0x48, 0xb2, 0x1d, 0xa8, 0xe0, 0xcf, 0xa7, 0xbe, + 0x1d, 0x5a, 0x83, 0x47, 0x91, 0x74, 0x97, 0x40, 0x68, 0x23, 0x99, 0x56, 0x68, 0x8d, 0xc2, 0xc1, + 0xd7, 0x22, 0xbc, 0xb5, 0xac, 0x49, 0x10, 0xf6, 0x3d, 0x60, 0xc7, 0xc6, 0xe8, 0x74, 0xe2, 0x53, + 0xb4, 0xc8, 0xd7, 0x73, 0xcb, 0xb7, 0xad, 0x28, 0x9c, 0xb5, 0x22, 0x75, 0x8a, 0xb6, 0x95, 0x90, + 0xfd, 0x88, 0x53, 0xa1, 0x75, 0x37, 0x35, 0xce, 0x5b, 0xde, 0x4c, 0x84, 0xf1, 0x89, 0x14, 0x7b, + 0x04, 0x57, 0x70, 0x73, 0x8c, 0xee, 0x3f, 0xe3, 0x1c, 0x12, 0xde, 0x55, 0xfe, 0x18, 0xd6, 0xa5, + 0x13, 0x23, 0x48, 0xae, 0x3c, 0x0a, 0xa7, 0xc0, 0x2e, 0x6c, 0x47, 0xa6, 0x87, 0x69, 0xcd, 0x2c, + 0xd7, 0xb4, 0xdc, 0x91, 0x1d, 0x5f, 0xa8, 0x5c, 0x9a, 0xfc, 0x97, 0x04, 0x71, 0x4b, 0xa2, 0x55, + 0x7f, 0x0b, 0x6a, 0xa9, 0xb1, 0x5b, 0x72, 0x0e, 0x0e, 0xda, 0xdd, 0x76, 0x73, 0xc8, 0x9d, 0x0d, + 0xc2, 0x69, 0xb5, 0x2e, 0x79, 0xb7, 0x72, 0x92, 0x13, 0x22, 0x4f, 0xbe, 0xb1, 0xb6, 0xf6, 0xb8, + 0xad, 0x14, 0xd4, 0x5c, 0x29, 0xab, 0x64, 0xd5, 0xbf, 0xba, 0x0e, 0x5b, 0x43, 0xdf, 0x70, 0x03, + 0x83, 0xeb, 0x41, 0x6e, 0xe8, 0x7b, 0x0e, 0xfb, 0x1e, 0x94, 0xc2, 0x91, 0x23, 0x4f, 0xa6, 0x5b, + 0x91, 0xe8, 0x5a, 0x20, 0x7d, 0x30, 0x1c, 0xf1, 0x63, 0x95, 0x62, 0xc8, 0x7f, 0xb0, 0x77, 0x20, + 0x7f, 0x6c, 0x4d, 0x6c, 0x57, 0x48, 0xef, 0xcb, 0x8b, 0x8c, 0xbb, 0x88, 0xdc, 0x5f, 0xd3, 0x38, + 0x15, 0x7b, 0x0f, 0x0a, 0x23, 0x6f, 0x1a, 0x6d, 0x9b, 0xc9, 0x05, 0x4a, 0xa9, 0x20, 0xc4, 0xee, + 0xaf, 0x69, 0x82, 0x8e, 0x7d, 0x04, 0x25, 0xdf, 0x73, 0x1c, 0x1c, 0x3b, 0xb1, 0xa1, 0xd6, 0x17, + 0x79, 0x34, 0x81, 0xdf, 0x5f, 0xd3, 0x62, 0x5a, 0xf5, 0x01, 0x14, 0x45, 0x65, 0xb1, 0x1b, 0x76, + 0xdb, 0x8f, 0x3b, 0xa2, 0x07, 0x9b, 0xfd, 0x83, 0x83, 0xce, 0x90, 0xdf, 0x0e, 0xd7, 0xfa, 0xdd, + 0xee, 0x6e, 0xa3, 0xf9, 0xb9, 0xb2, 0xbe, 0x5b, 0x82, 0x02, 0x1f, 0x62, 0xf5, 0x77, 0x32, 0xb0, + 0xb9, 0xd0, 0x00, 0xf6, 0x09, 0xe4, 0xc8, 0x46, 0xe3, 0xdd, 0x73, 0x67, 0x65, 0x2b, 0xa5, 0x34, + 0xd7, 0xd6, 0x91, 0x43, 0xfd, 0x14, 0x36, 0xd2, 0x70, 0xc9, 0x21, 0x55, 0x83, 0xb2, 0xd6, 0x6e, + 0xb4, 0xf4, 0x7e, 0xaf, 0xfb, 0x25, 0xf7, 0xef, 0x52, 0xf2, 0xa9, 0xd6, 0x21, 0x1f, 0xd2, 0x6f, + 0x82, 0xb2, 0xd8, 0x31, 0xec, 0x31, 0x5a, 0x5e, 0xd3, 0x99, 0x63, 0x91, 0x82, 0x2b, 0x0d, 0xd9, + 0xcd, 0x15, 0x3d, 0x29, 0xc8, 0x78, 0xa8, 0xd4, 0x28, 0x95, 0x56, 0x7f, 0x0b, 0xd8, 0x72, 0x0f, + 0xfe, 0xea, 0xb2, 0xff, 0xef, 0x19, 0xc8, 0x1d, 0x3a, 0x86, 0xcb, 0x6e, 0x43, 0x9e, 0xde, 0x6a, + 0x12, 0x7b, 0x80, 0xbc, 0x22, 0x71, 0x5a, 0x10, 0x8e, 0xbd, 0x05, 0xd9, 0x70, 0x14, 0x5d, 0x4a, + 0xbf, 0xfa, 0x9c, 0xc9, 0xb7, 0xbf, 0xa6, 0x21, 0x15, 0xbb, 0x07, 0x59, 0xd3, 0x8c, 0x2e, 0x68, + 0x08, 0xb7, 0x54, 0xcb, 0x08, 0x8d, 0x96, 0x35, 0xb6, 0x5d, 0x5b, 0xbc, 0x2d, 0x85, 0x24, 0xec, + 0x75, 0xc8, 0x9a, 0x23, 0x27, 0x7d, 0xdb, 0x86, 0x3b, 0x22, 0xe2, 0x0c, 0xcd, 0x91, 0x83, 0xfa, + 0x63, 0xe8, 0x5f, 0xe8, 0xfe, 0xdc, 0xa5, 0x98, 0xdb, 0x40, 0xd8, 0x74, 0x15, 0xd4, 0x84, 0xe6, + 0x14, 0xb8, 0x1b, 0x88, 0xdb, 0xad, 0x33, 0xdf, 0x9a, 0x19, 0x7e, 0x6c, 0xcd, 0xd9, 0xc1, 0x21, + 0x07, 0xec, 0x16, 0x80, 0x1e, 0xe4, 0x55, 0xdf, 0xa6, 0x87, 0x84, 0xd0, 0x2c, 0x50, 0xa3, 0x5f, + 0x2b, 0xde, 0x6e, 0x14, 0x18, 0xf5, 0xcf, 0xb3, 0x50, 0x91, 0xea, 0xc3, 0x3e, 0x80, 0x92, 0x99, + 0x5e, 0x88, 0xd7, 0x96, 0x2a, 0xfd, 0xa0, 0x15, 0x2d, 0x41, 0x53, 0x4c, 0x6f, 0x3a, 0xb3, 0x0b, + 0xf5, 0x67, 0x86, 0x6f, 0xf3, 0x07, 0xea, 0xd6, 0xe5, 0xc3, 0xb3, 0x81, 0x15, 0x3e, 0x89, 0x30, + 0xfb, 0x6b, 0x5a, 0x35, 0x90, 0xd2, 0x64, 0xbb, 0x88, 0x26, 0x65, 0x53, 0x8f, 0x02, 0x72, 0xe0, + 0xfe, 0x9a, 0x16, 0xe1, 0x91, 0xd4, 0x3a, 0xb7, 0x46, 0xf3, 0x30, 0xb2, 0x5d, 0x6a, 0x51, 0x83, + 0x08, 0x48, 0x2f, 0x93, 0xf2, 0x9f, 0xec, 0x21, 0x4a, 0x6c, 0xc3, 0x71, 0x3c, 0x52, 0xf8, 0xf2, + 0xf2, 0x51, 0x56, 0x2b, 0x86, 0xf3, 0x97, 0x50, 0xa3, 0x14, 0xbb, 0x0b, 0x79, 0x2f, 0x3c, 0xb1, + 0x22, 0x8b, 0x20, 0x7a, 0x92, 0x08, 0x41, 0xad, 0x66, 0x17, 0x67, 0x0a, 0xa1, 0xd5, 0x3f, 0xcc, + 0x40, 0x51, 0xf4, 0x00, 0xdb, 0x82, 0xda, 0xa0, 0x3d, 0xd4, 0x9f, 0x34, 0xb4, 0x4e, 0x63, 0xb7, + 0xdb, 0x16, 0x97, 0x84, 0x1e, 0x6b, 0x8d, 0x9e, 0x10, 0x90, 0x5a, 0xfb, 0x49, 0xff, 0xf3, 0x36, + 0xf7, 0xf2, 0xb6, 0xda, 0xbd, 0x2f, 0x95, 0x2c, 0x3f, 0xd2, 0x68, 0x1f, 0x36, 0x34, 0x94, 0x95, + 0x15, 0x28, 0xb6, 0xbf, 0x68, 0x37, 0x8f, 0x48, 0x58, 0x6e, 0x00, 0xb4, 0xda, 0x8d, 0x6e, 0xb7, + 0xdf, 0x44, 0xe1, 0x59, 0x60, 0x0c, 0x36, 0x9a, 0x5a, 0xbb, 0x31, 0x6c, 0xeb, 0x8d, 0x66, 0xb3, + 0x7f, 0xd4, 0x1b, 0x2a, 0x45, 0x2c, 0xb1, 0xd1, 0x1d, 0xb6, 0xb5, 0x18, 0x44, 0xcf, 0xc4, 0xb5, + 0xb4, 0xfe, 0x61, 0x0c, 0x29, 0xef, 0x96, 0xd1, 0x8e, 0xa4, 0xb1, 0x52, 0xff, 0x78, 0x0b, 0x36, + 0xd2, 0x53, 0x93, 0x7d, 0x0c, 0x25, 0xd3, 0x4c, 0x8d, 0xf1, 0x8d, 0x55, 0x53, 0xf8, 0x41, 0xcb, + 0x8c, 0x86, 0x99, 0xff, 0x60, 0xaf, 0x45, 0x0b, 0x69, 0x7d, 0x69, 0x21, 0x45, 0xcb, 0xe8, 0x07, + 0xb0, 0x29, 0xde, 0xc9, 0x31, 0x8d, 0xd0, 0x38, 0x36, 0x02, 0x2b, 0xbd, 0x4a, 0x9a, 0x84, 0x6c, + 0x09, 0xdc, 0xfe, 0x9a, 0xb6, 0x31, 0x4a, 0x41, 0xd8, 0xf7, 0x61, 0xc3, 0xe0, 0x1e, 0xa4, 0x88, + 0x3f, 0x27, 0xab, 0xb2, 0x0d, 0x72, 0x24, 0x25, 0xec, 0x35, 0x43, 0x06, 0xe0, 0x44, 0x34, 0x7d, + 0x6f, 0x96, 0x30, 0xe7, 0x53, 0xa7, 0xb8, 0xbe, 0x37, 0x93, 0x78, 0xab, 0xa6, 0x94, 0x66, 0x1f, + 0x41, 0x55, 0xd4, 0x3c, 0x71, 0x97, 0xc4, 0x4b, 0x96, 0x57, 0x9b, 0x54, 0xd3, 0xfd, 0x35, 0xad, + 0x32, 0x4a, 0x92, 0xec, 0x11, 0xea, 0xa3, 0x89, 0x22, 0x5f, 0x94, 0xe7, 0x1a, 0xd5, 0x36, 0xe2, + 0x02, 0x23, 0x4e, 0xb1, 0xf7, 0x00, 0xa8, 0x9e, 0x9c, 0xa7, 0x94, 0x8a, 0x6f, 0xf2, 0xbd, 0x59, + 0xc4, 0x52, 0x36, 0xa3, 0x84, 0x54, 0x3d, 0xee, 0xec, 0x2a, 0x2f, 0x57, 0x8f, 0x1c, 0x5e, 0x49, + 0xf5, 0xb8, 0x9f, 0x2c, 0xae, 0x1e, 0x67, 0x83, 0xa5, 0xea, 0x45, 0x5c, 0xbc, 0x7a, 0x9c, 0x29, + 0xaa, 0x1e, 0xe7, 0xa9, 0x2c, 0x56, 0x2f, 0x62, 0xa1, 0xea, 0x71, 0x8e, 0xef, 0x2f, 0x59, 0x20, + 0xd5, 0xe7, 0x5a, 0x20, 0x38, 0x6c, 0x69, 0x1b, 0xe4, 0xfb, 0xb0, 0x11, 0x9c, 0x78, 0x67, 0x92, + 0x00, 0xa9, 0xc9, 0xdc, 0x83, 0x13, 0xef, 0x4c, 0x96, 0x20, 0xb5, 0x40, 0x06, 0x60, 0x6d, 0x79, + 0x13, 0xe9, 0xc4, 0x75, 0x43, 0xae, 0x2d, 0xb5, 0xf0, 0x89, 0x6d, 0x9d, 0x61, 0x6d, 0x8d, 0x28, + 0x81, 0x9d, 0x92, 0xb8, 0x8e, 0x02, 0xe1, 0x0c, 0x4a, 0x45, 0x05, 0x89, 0x92, 0x20, 0x76, 0x22, + 0x05, 0x38, 0xb7, 0xe6, 0xae, 0xcc, 0xa6, 0xc8, 0x73, 0xeb, 0xc8, 0x4d, 0x31, 0x56, 0x39, 0xa9, + 0x60, 0x4d, 0x56, 0x45, 0x60, 0x7d, 0x3d, 0xb7, 0xdc, 0x91, 0x25, 0xa2, 0xff, 0x52, 0xab, 0x62, + 0x20, 0x70, 0xc9, 0xaa, 0x88, 0x20, 0xf1, 0xbc, 0x8e, 0xd9, 0xd9, 0xe2, 0xbc, 0x96, 0x98, 0x69, + 0x5e, 0xc7, 0xac, 0xf1, 0x82, 0x8a, 0x79, 0x2f, 0x2d, 0x2d, 0x28, 0x89, 0x99, 0x2f, 0xa8, 0x98, + 0xfb, 0x11, 0x88, 0xd9, 0xc4, 0x3b, 0x37, 0x15, 0x23, 0xc8, 0x6b, 0x2d, 0x7a, 0x17, 0x46, 0x71, + 0x0a, 0xe7, 0xaa, 0x6f, 0xa1, 0xc5, 0x23, 0xa6, 0xc2, 0x65, 0x79, 0xae, 0x6a, 0x84, 0x89, 0x97, + 0x92, 0x9f, 0x24, 0xa5, 0xc2, 0x66, 0x76, 0xe8, 0xd7, 0xcd, 0xe5, 0xc2, 0x0e, 0xed, 0xd0, 0x4f, + 0x0a, 0xc3, 0x14, 0x7b, 0x07, 0x68, 0x1a, 0x72, 0x16, 0x4b, 0x16, 0xdd, 0xd8, 0x2d, 0x82, 0xa1, + 0x64, 0x8a, 0xdf, 0x38, 0x59, 0x44, 0x19, 0x23, 0x73, 0x54, 0x1f, 0xcb, 0x93, 0x85, 0x17, 0xd1, + 0x6c, 0x35, 0x71, 0xb2, 0x70, 0xa2, 0xa6, 0x39, 0x62, 0xf7, 0x81, 0xb8, 0x89, 0x7e, 0x92, 0x7a, + 0xf2, 0xce, 0xf7, 0x66, 0x9c, 0xba, 0x88, 0x04, 0x48, 0x8b, 0x2d, 0x70, 0x3c, 0x37, 0x6a, 0xf8, + 0x49, 0xaa, 0x05, 0x88, 0x88, 0x85, 0xc1, 0x28, 0x4e, 0xa9, 0xbf, 0x57, 0x80, 0xa2, 0x90, 0xb5, + 0xec, 0x12, 0x6c, 0x0a, 0x91, 0xdf, 0x6a, 0x0c, 0x1b, 0xbb, 0x8d, 0x01, 0x2a, 0x69, 0x0c, 0x36, + 0xb8, 0xcc, 0x8f, 0x61, 0x19, 0xdc, 0x07, 0x48, 0xe8, 0xc7, 0xa0, 0x75, 0xdc, 0x07, 0x04, 0x2f, + 0x7f, 0x5a, 0x34, 0xcb, 0x36, 0xa1, 0xc2, 0x19, 0x39, 0x80, 0xee, 0x2c, 0x13, 0x17, 0x4f, 0xe7, + 0x25, 0x16, 0x7e, 0xe8, 0x59, 0x48, 0x58, 0x38, 0xa0, 0x18, 0xb3, 0x44, 0xa7, 0xa2, 0x0c, 0x36, + 0x86, 0xda, 0x51, 0xaf, 0x99, 0x94, 0x53, 0xa6, 0x7b, 0xa6, 0x3c, 0x9b, 0x27, 0x9d, 0xf6, 0x53, + 0x05, 0x90, 0x89, 0xe7, 0x42, 0xe9, 0x0a, 0xaa, 0x99, 0x94, 0x09, 0x25, 0xab, 0xec, 0x2a, 0x5c, + 0x1a, 0xec, 0xf7, 0x9f, 0xea, 0x9c, 0x29, 0x6e, 0x42, 0x8d, 0x6d, 0x83, 0x22, 0x21, 0x78, 0xf6, + 0x1b, 0x58, 0x24, 0x41, 0x23, 0xc2, 0x81, 0xb2, 0x49, 0x01, 0x04, 0x08, 0x1b, 0xf2, 0x7d, 0x57, + 0xc1, 0xa6, 0x70, 0xd6, 0x7e, 0xf7, 0xe8, 0xa0, 0x37, 0x50, 0xb6, 0xb0, 0x12, 0x04, 0xe1, 0x35, + 0x67, 0x71, 0x36, 0xc9, 0x6e, 0x7d, 0x89, 0x36, 0x70, 0x84, 0x3d, 0x6d, 0x68, 0xbd, 0x4e, 0xef, + 0xf1, 0x40, 0xd9, 0x8e, 0x73, 0x6e, 0x6b, 0x5a, 0x5f, 0x1b, 0x28, 0x97, 0x63, 0xc0, 0x60, 0xd8, + 0x18, 0x1e, 0x0d, 0x94, 0x2b, 0x71, 0x2d, 0x0f, 0xb5, 0x7e, 0xb3, 0x3d, 0x18, 0x74, 0x3b, 0x83, + 0xa1, 0x72, 0x95, 0x5d, 0x86, 0xad, 0xa4, 0x46, 0x11, 0x71, 0x5d, 0xaa, 0xa8, 0xf6, 0xb8, 0x3d, + 0x54, 0xae, 0xc5, 0xd5, 0x68, 0xf6, 0xbb, 0xdd, 0x06, 0x9d, 0x8e, 0x5f, 0x47, 0x22, 0x0a, 0x08, + 0x10, 0xad, 0x79, 0x05, 0xeb, 0x75, 0xd4, 0x93, 0x41, 0x37, 0xa4, 0xa9, 0x31, 0x68, 0xff, 0xe8, + 0xa8, 0xdd, 0x6b, 0xb6, 0x95, 0x57, 0x93, 0xa9, 0x11, 0xc3, 0x6e, 0xc6, 0x53, 0x23, 0x06, 0xdd, + 0x8a, 0xcb, 0x8c, 0x40, 0x03, 0x65, 0x07, 0xf3, 0x13, 0xf5, 0xe8, 0xf5, 0xda, 0xcd, 0x21, 0xb6, + 0xf5, 0xb5, 0xb8, 0x17, 0x8f, 0x0e, 0x1f, 0x6b, 0x8d, 0x56, 0x5b, 0x51, 0x11, 0xa2, 0xb5, 0x7b, + 0x8d, 0x83, 0x68, 0xb4, 0x6f, 0x4b, 0xa3, 0x7d, 0xd8, 0x19, 0x6a, 0xca, 0x9d, 0x78, 0x74, 0x29, + 0xf9, 0x3a, 0x7b, 0x05, 0xae, 0xca, 0xf3, 0x50, 0x7f, 0xda, 0x19, 0xee, 0x8b, 0xc3, 0xfc, 0xbb, + 0xfc, 0x20, 0x9a, 0x90, 0xcd, 0x56, 0x93, 0x47, 0x2d, 0x10, 0x2f, 0xa6, 0xee, 0xed, 0x56, 0xe9, + 0xbd, 0x7a, 0xa1, 0x80, 0xa8, 0x9f, 0x01, 0x93, 0x9f, 0x63, 0x16, 0x41, 0xd4, 0x0c, 0x72, 0x63, + 0xdf, 0x9b, 0x46, 0xef, 0x87, 0xe0, 0x6f, 0xb4, 0xe1, 0x67, 0xf3, 0x63, 0x3a, 0x20, 0x4f, 0xde, + 0x07, 0x90, 0x41, 0xea, 0xdf, 0xca, 0xc0, 0x46, 0x5a, 0xf9, 0x20, 0xa7, 0xed, 0x58, 0x77, 0xbd, + 0x90, 0xbf, 0xf1, 0x16, 0xc4, 0xaf, 0x24, 0x8f, 0x7b, 0x5e, 0x48, 0x8f, 0xbc, 0x91, 0x4b, 0x21, + 0xd6, 0x25, 0x78, 0xae, 0x71, 0x9a, 0x75, 0xe0, 0x52, 0xea, 0xf5, 0xeb, 0xd4, 0x0b, 0x7b, 0xf5, + 0xf8, 0x9d, 0xd8, 0x85, 0xfa, 0x6b, 0x2c, 0x58, 0x6e, 0x93, 0x78, 0xe5, 0x21, 0x97, 0xbc, 0xf2, + 0xb0, 0x0f, 0xb5, 0x94, 0xae, 0x43, 0x9e, 0xa0, 0x71, 0xba, 0xa6, 0x25, 0x7b, 0xfc, 0xf2, 0x6a, + 0xaa, 0x7f, 0x23, 0x03, 0x55, 0x59, 0xf3, 0xf9, 0xce, 0x39, 0x51, 0x6c, 0x93, 0xf8, 0xad, 0xdb, + 0x66, 0xf4, 0x60, 0x5b, 0x04, 0xea, 0xd0, 0x77, 0x3a, 0xb8, 0xff, 0x7b, 0xef, 0x74, 0x10, 0x37, + 0x47, 0x06, 0xb1, 0x9b, 0x00, 0xf4, 0x98, 0xd2, 0xde, 0xe7, 0x48, 0x20, 0x9e, 0x29, 0x4f, 0x20, + 0xea, 0x2d, 0x28, 0xef, 0x9d, 0x46, 0xa1, 0x5e, 0xf2, 0x4b, 0x87, 0x65, 0xfe, 0xa8, 0x84, 0xfa, + 0x47, 0x19, 0xd8, 0x48, 0xfc, 0x1e, 0x14, 0x9a, 0xc0, 0x5f, 0x4d, 0xe7, 0xd3, 0x61, 0xdd, 0x3c, + 0x4e, 0xce, 0x50, 0xd7, 0xe5, 0x33, 0xd4, 0xdb, 0x22, 0xb3, 0xac, 0x2c, 0xf2, 0xe3, 0xb2, 0xc4, + 0x93, 0x15, 0x8f, 0xa0, 0x8a, 0xff, 0x35, 0x6b, 0x6c, 0xf9, 0xbe, 0x65, 0xa6, 0xaf, 0xb6, 0x24, + 0xc4, 0x29, 0x22, 0xb2, 0xf1, 0xc4, 0xe9, 0xec, 0x73, 0x5e, 0xa3, 0xa2, 0x57, 0xd2, 0xfe, 0x6b, + 0x16, 0x2a, 0x92, 0x1e, 0xf9, 0x8d, 0xa6, 0xdf, 0x0d, 0x28, 0x27, 0x6f, 0x23, 0x89, 0x77, 0x02, + 0x62, 0x40, 0x6a, 0xac, 0xb2, 0x0b, 0x63, 0x55, 0x87, 0xa2, 0xcf, 0x2f, 0xdc, 0x0a, 0x37, 0x74, + 0x94, 0x4c, 0x3b, 0x7c, 0xf3, 0x2f, 0x39, 0xa3, 0x79, 0x1f, 0xaa, 0x92, 0xb7, 0x36, 0xa8, 0x17, + 0xe4, 0x07, 0xb2, 0x62, 0xfa, 0x4a, 0xe2, 0xb9, 0x0d, 0xd8, 0x65, 0x28, 0x8c, 0x4f, 0x75, 0xf3, + 0x98, 0xdf, 0x1e, 0x2f, 0x6b, 0xf9, 0xf1, 0x69, 0xeb, 0x98, 0x4e, 0xb0, 0xc6, 0xb1, 0xea, 0xc4, + 0x7d, 0x68, 0xa5, 0x71, 0xa4, 0x20, 0xdd, 0x83, 0xe2, 0xf8, 0x54, 0xbe, 0x05, 0xbe, 0xd4, 0xe5, + 0x85, 0xf1, 0x29, 0xdd, 0xfd, 0x7e, 0x17, 0xb6, 0xc5, 0xfe, 0x6d, 0x04, 0x3a, 0x7f, 0x92, 0x85, + 0xde, 0xcc, 0xe2, 0xef, 0x1e, 0x6e, 0x71, 0x5c, 0x23, 0x18, 0x10, 0x06, 0x67, 0x9c, 0x0a, 0x55, + 0x69, 0x02, 0x72, 0x5f, 0x58, 0x59, 0x4b, 0xc1, 0xd8, 0x27, 0x50, 0x1d, 0x9f, 0xf2, 0x01, 0x1d, + 0x7a, 0x07, 0x96, 0xb8, 0x6d, 0xb2, 0xbd, 0x38, 0x94, 0x14, 0x3b, 0x92, 0xa2, 0x64, 0x57, 0xa0, + 0xa0, 0x19, 0x67, 0x83, 0x1f, 0x75, 0x49, 0x89, 0x2c, 0x6b, 0x22, 0xf5, 0x59, 0xae, 0xb4, 0xa1, + 0x6c, 0xaa, 0xff, 0x30, 0x03, 0x1b, 0x89, 0x0d, 0x80, 0x8b, 0x90, 0xdd, 0x97, 0x3f, 0x77, 0x50, + 0x5f, 0x34, 0x13, 0x90, 0xe4, 0xc1, 0xf0, 0x62, 0xc6, 0x9f, 0xe1, 0x5d, 0xf5, 0x20, 0xdc, 0x2a, + 0xf7, 0x79, 0x76, 0xe5, 0xe3, 0xe9, 0x8f, 0x21, 0x3b, 0xbc, 0x98, 0x71, 0x7f, 0x13, 0x6e, 0x89, + 0xdc, 0x36, 0xe5, 0x9b, 0x21, 0x05, 0x2a, 0x7d, 0xde, 0xfe, 0x92, 0xbf, 0x6b, 0x72, 0xa8, 0x75, + 0x0e, 0x1a, 0xda, 0x97, 0x3a, 0x02, 0x48, 0x69, 0xd8, 0xeb, 0x6b, 0xed, 0xce, 0xe3, 0x1e, 0x01, + 0x72, 0xe4, 0x8d, 0x4a, 0xaa, 0xd8, 0x30, 0xcd, 0xbd, 0x53, 0xf9, 0x2d, 0xad, 0x4c, 0xea, 0x2d, + 0xad, 0xf8, 0xd9, 0x39, 0xf9, 0xb9, 0xce, 0x30, 0xf6, 0xe9, 0xb3, 0x78, 0x15, 0xc6, 0x4b, 0x9a, + 0xbd, 0x01, 0xb9, 0xf1, 0xa9, 0x75, 0x91, 0x36, 0xf4, 0xd2, 0x0b, 0x88, 0x08, 0xd4, 0x5f, 0x64, + 0x80, 0xa5, 0x2a, 0xc2, 0x6d, 0x8f, 0xef, 0x5a, 0x97, 0x8f, 0xa1, 0x2e, 0xa2, 0x39, 0x39, 0x95, + 0xe4, 0xaf, 0x17, 0x5d, 0x7a, 0xd9, 0x4b, 0xa2, 0x9c, 0x93, 0x37, 0xeb, 0xd8, 0xbb, 0xc0, 0x5f, + 0xe2, 0xa4, 0x50, 0xa3, 0xdc, 0x73, 0xec, 0x44, 0x2d, 0xa1, 0x49, 0x9e, 0xde, 0x94, 0x9f, 0x14, + 0xe5, 0xae, 0xfe, 0xcd, 0x64, 0xd4, 0x68, 0xcd, 0xab, 0x7f, 0x29, 0x03, 0x97, 0xd2, 0x13, 0xe2, + 0x97, 0x6b, 0x65, 0xfa, 0xfd, 0xd4, 0xec, 0xe2, 0xfb, 0xa9, 0xab, 0xe6, 0x53, 0x6e, 0xe5, 0x7c, + 0xfa, 0xdd, 0x0c, 0x6c, 0x4b, 0xbd, 0x9f, 0x58, 0x8b, 0x7f, 0x41, 0x35, 0x93, 0x9e, 0x51, 0xcd, + 0xa5, 0x9e, 0x51, 0x55, 0x7f, 0x9e, 0x81, 0x2b, 0x0b, 0x35, 0xd1, 0xac, 0xbf, 0xd0, 0xba, 0xa4, + 0x9f, 0x5b, 0xa5, 0xe3, 0x86, 0xe8, 0xed, 0x8f, 0xcc, 0xbd, 0xac, 0xf4, 0xdc, 0x2a, 0x9d, 0x41, + 0xd1, 0x41, 0xe8, 0xab, 0xe2, 0x89, 0x28, 0x3d, 0xb8, 0x70, 0x47, 0x62, 0xb0, 0xcb, 0x04, 0x19, + 0x5c, 0xb8, 0x23, 0xf5, 0x8f, 0x33, 0x70, 0x6d, 0xa1, 0x0d, 0x8d, 0x79, 0xe8, 0x89, 0xf3, 0xe4, + 0xbf, 0xa0, 0x66, 0xdc, 0x82, 0x0a, 0x9d, 0xb6, 0x8b, 0x43, 0x6a, 0xde, 0xad, 0x60, 0x24, 0xe5, + 0x2a, 0x90, 0x35, 0x8d, 0x0b, 0xf1, 0xf0, 0x01, 0xfe, 0xc4, 0x05, 0x7b, 0xe2, 0xcd, 0x7d, 0x11, + 0x61, 0x44, 0xbf, 0xd5, 0x0f, 0x60, 0x2b, 0xa9, 0x7a, 0x53, 0xbc, 0x7a, 0x7b, 0x0b, 0x2a, 0xae, + 0x75, 0xa6, 0x47, 0x6f, 0xe2, 0x8a, 0xb8, 0x39, 0xd7, 0x3a, 0x13, 0x04, 0xea, 0x9e, 0x2c, 0x0b, + 0xe3, 0x2f, 0x8b, 0x38, 0x66, 0x2a, 0xf6, 0xc7, 0x73, 0xcc, 0x08, 0x85, 0xb9, 0x49, 0xad, 0x2c, + 0xba, 0xd6, 0x19, 0xcd, 0xc3, 0x33, 0x91, 0x4f, 0xc3, 0x14, 0x6f, 0xd0, 0xae, 0x7c, 0x51, 0xee, + 0x1a, 0x94, 0x66, 0x7e, 0xaa, 0x9b, 0x8a, 0x33, 0x9f, 0x17, 0x7b, 0x47, 0x44, 0xe5, 0x3d, 0x2f, + 0x56, 0x82, 0xc7, 0xe9, 0x89, 0x2f, 0x0f, 0xe5, 0x92, 0x2f, 0x0f, 0x7d, 0x28, 0xc4, 0x20, 0xd9, + 0x7d, 0xbc, 0x64, 0x05, 0xb2, 0xb6, 0x79, 0x4e, 0x05, 0xd7, 0x34, 0xfc, 0x49, 0x9a, 0x9c, 0xf5, + 0xb5, 0x08, 0x0c, 0xc4, 0x9f, 0xea, 0x2e, 0x54, 0xb4, 0x94, 0x91, 0x5b, 0x95, 0xfc, 0x45, 0x41, + 0xfa, 0xd1, 0xad, 0xa4, 0x83, 0xb4, 0x4a, 0xe2, 0x2e, 0x0a, 0xd4, 0x40, 0x08, 0xbe, 0x27, 0x86, + 0x3f, 0x3a, 0x31, 0xfc, 0xae, 0xe5, 0x4e, 0xc2, 0x13, 0xec, 0x72, 0xee, 0xc6, 0x95, 0xbb, 0x10, + 0x38, 0x28, 0x9a, 0x0e, 0xd8, 0x8b, 0x0e, 0x91, 0x47, 0xdf, 0x29, 0x71, 0xad, 0x33, 0xc1, 0xff, + 0x2a, 0x00, 0xf6, 0xbf, 0x40, 0xf3, 0x63, 0xcc, 0xb2, 0xe7, 0x98, 0x1c, 0xad, 0x6e, 0x89, 0xf6, + 0x8a, 0x87, 0x3b, 0x5a, 0xd6, 0x58, 0x75, 0xc4, 0xc8, 0xf3, 0x06, 0x89, 0x4e, 0xf8, 0x4e, 0xc3, + 0xc8, 0x5e, 0x83, 0x6a, 0xe4, 0x91, 0xa0, 0x77, 0xde, 0x78, 0xf1, 0x95, 0x08, 0xd6, 0x9b, 0x4f, + 0xd5, 0x3f, 0xc8, 0x42, 0xb5, 0xc1, 0xa3, 0x83, 0x66, 0x17, 0xfd, 0x59, 0xc8, 0x7e, 0x0b, 0x2e, + 0xd3, 0xc3, 0x2f, 0xfc, 0x35, 0x67, 0x0a, 0xca, 0xa1, 0x10, 0x7f, 0xd1, 0x89, 0xf7, 0xa5, 0x4e, + 0x14, 0x2c, 0x0f, 0x06, 0xa7, 0xf6, 0x8c, 0xdf, 0x2c, 0xe9, 0x98, 0xe7, 0x74, 0x8d, 0x83, 0xc7, + 0x17, 0xd0, 0x1b, 0x31, 0x69, 0x04, 0xbd, 0xdf, 0x80, 0xd9, 0xcf, 0x4e, 0x45, 0xb6, 0x22, 0xf4, + 0x02, 0x81, 0x87, 0xa7, 0x9c, 0xe6, 0x3e, 0x6c, 0x89, 0xf7, 0x5a, 0x96, 0x36, 0xe0, 0x4d, 0x8e, + 0x48, 0xe6, 0xf7, 0x00, 0xb6, 0xc4, 0x3b, 0x35, 0xf4, 0x70, 0xa9, 0x3e, 0xf2, 0x66, 0x17, 0xe2, + 0xf8, 0xf2, 0x8d, 0xe7, 0x54, 0xb5, 0xc3, 0x49, 0x11, 0xc4, 0xeb, 0xb9, 0x19, 0xa4, 0xa1, 0xd7, + 0xdb, 0x70, 0xf5, 0x39, 0x6d, 0x7a, 0x59, 0x88, 0x44, 0x49, 0x0a, 0x91, 0xb8, 0xbe, 0x0b, 0xdb, + 0xab, 0xca, 0xfb, 0x36, 0x79, 0xa8, 0x3f, 0xab, 0x01, 0x24, 0x33, 0x36, 0xa5, 0x8e, 0x66, 0x16, + 0xd4, 0xd1, 0x6f, 0x15, 0x18, 0xf4, 0x01, 0x6c, 0x60, 0x57, 0xe9, 0x09, 0x47, 0x76, 0x25, 0x47, + 0x15, 0xa9, 0x86, 0xc9, 0x7d, 0xd9, 0xe5, 0xb0, 0x8a, 0xdc, 0xca, 0xb0, 0x8a, 0xf7, 0xa1, 0xc8, + 0x0f, 0xda, 0x02, 0x71, 0x45, 0xfb, 0xea, 0xe2, 0xea, 0x7b, 0x20, 0x6e, 0xa9, 0x44, 0x74, 0xac, + 0x0d, 0x1b, 0x28, 0xfa, 0x7d, 0x3b, 0x3c, 0x99, 0xca, 0x17, 0xb6, 0x6f, 0x2e, 0x73, 0x46, 0x64, + 0xe2, 0xe3, 0x30, 0x72, 0x52, 0xd2, 0x5e, 0xc3, 0xa9, 0xf0, 0xfe, 0x92, 0xf6, 0x5a, 0x94, 0xb5, + 0xd7, 0xe1, 0x94, 0xfb, 0x7c, 0x51, 0x7b, 0x7d, 0x07, 0x2e, 0x89, 0x9b, 0x73, 0xc8, 0x80, 0xdd, + 0x49, 0xf4, 0x3c, 0x50, 0x53, 0x11, 0xef, 0xb6, 0x4c, 0xc9, 0xb6, 0x43, 0xf2, 0x2f, 0x60, 0x7b, + 0x74, 0x62, 0xb8, 0x13, 0x4b, 0x0f, 0x8f, 0x1d, 0x9d, 0x3e, 0x21, 0xa1, 0x4f, 0x8d, 0x99, 0x50, + 0xaa, 0xdf, 0x58, 0xaa, 0x6c, 0x93, 0x88, 0x87, 0xc7, 0x0e, 0x05, 0xb1, 0xc5, 0xc1, 0x37, 0x5b, + 0xa3, 0x45, 0xf8, 0xc2, 0x19, 0x38, 0x2c, 0x9d, 0x81, 0x2f, 0xaa, 0xd9, 0x95, 0x15, 0x6a, 0x76, + 0xa2, 0x2c, 0x57, 0x65, 0x65, 0x99, 0xbd, 0x0d, 0x45, 0x71, 0xf1, 0x57, 0xf8, 0x7d, 0xd9, 0xf2, + 0xea, 0xd0, 0x22, 0x12, 0x2c, 0x29, 0x8a, 0xc8, 0xa0, 0xb7, 0x21, 0x36, 0x78, 0x49, 0x32, 0x8c, + 0xed, 0x0a, 0xa7, 0x67, 0x1c, 0x70, 0x27, 0x7c, 0xbc, 0xd7, 0xa5, 0x8c, 0x63, 0x9c, 0xb0, 0xcb, + 0x17, 0x38, 0xae, 0xff, 0x83, 0x02, 0x14, 0xc4, 0xb9, 0xfa, 0x7d, 0xc8, 0x99, 0xbe, 0x37, 0x8b, + 0x23, 0xd6, 0x57, 0x68, 0xed, 0xf4, 0x0d, 0x43, 0x54, 0xf0, 0x1f, 0x40, 0xc1, 0x30, 0x4d, 0x7d, + 0x7c, 0x9a, 0x3e, 0x8f, 0x5e, 0x50, 0xa0, 0xf7, 0xd7, 0xb4, 0xbc, 0x41, 0x9a, 0xf4, 0xc7, 0x50, + 0x46, 0xfa, 0x24, 0x84, 0xb5, 0xb2, 0x6c, 0x16, 0x44, 0xaa, 0xee, 0xfe, 0x9a, 0x56, 0x32, 0x22, + 0xb5, 0xf7, 0xd7, 0xd3, 0x9e, 0xfd, 0xdc, 0x52, 0x03, 0x17, 0xf4, 0xb4, 0x05, 0x1f, 0xff, 0x6f, + 0x00, 0x77, 0xf5, 0xc6, 0x3b, 0x76, 0x5e, 0x3e, 0xfa, 0x5c, 0xda, 0xdf, 0xf7, 0xd7, 0x34, 0xbe, + 0x6f, 0x45, 0xfb, 0xfd, 0x87, 0x91, 0xd7, 0x3d, 0xfe, 0x7e, 0xd3, 0x8a, 0x9e, 0x41, 0x31, 0x18, + 0xbb, 0xde, 0x49, 0x26, 0x22, 0x9b, 0x19, 0x3d, 0x2a, 0x2f, 0x4e, 0x4b, 0x64, 0xb6, 0x78, 0x57, + 0x27, 0xb6, 0x78, 0x8b, 0xff, 0x04, 0x2a, 0xdc, 0x09, 0xcb, 0xf9, 0x4a, 0x4b, 0x5d, 0x9b, 0x6c, + 0xca, 0x74, 0xac, 0x97, 0x6c, 0xd1, 0xcd, 0xa8, 0x9d, 0xbe, 0x25, 0x9f, 0x9c, 0xdc, 0x58, 0xd9, + 0x51, 0x5a, 0x7c, 0x88, 0xc2, 0x1b, 0xab, 0x71, 0x1e, 0xd6, 0x85, 0x6d, 0x71, 0xc4, 0xc0, 0x37, + 0xe0, 0x68, 0xcf, 0x84, 0xa5, 0xf1, 0x4a, 0xed, 0xd0, 0xfb, 0x6b, 0x1a, 0x33, 0x96, 0xf7, 0xed, + 0x26, 0x6c, 0x45, 0x55, 0xa2, 0x9d, 0x55, 0x0a, 0xbd, 0x92, 0x9b, 0x94, 0xec, 0xbb, 0xfb, 0x6b, + 0xda, 0xa6, 0x91, 0x06, 0xb1, 0x0e, 0x5c, 0x8a, 0x32, 0x21, 0x57, 0xbb, 0xe8, 0x99, 0xea, 0xd2, + 0x28, 0xca, 0x7b, 0xf5, 0xfe, 0x9a, 0xb6, 0x65, 0x2c, 0x6d, 0xe0, 0x07, 0x51, 0x7d, 0x64, 0xe5, + 0x90, 0xaf, 0xc4, 0x5b, 0x2b, 0xbb, 0x29, 0xd1, 0x54, 0xe3, 0x9a, 0x25, 0xa0, 0x24, 0x8e, 0xe1, + 0xba, 0x06, 0x57, 0x56, 0x4b, 0x18, 0x79, 0x9b, 0xc9, 0xf1, 0x6d, 0x46, 0x95, 0xb7, 0x99, 0xc5, + 0xb7, 0x5e, 0xa4, 0x4d, 0xe7, 0x87, 0x50, 0x4b, 0x89, 0x58, 0x56, 0x81, 0x62, 0xf4, 0xa0, 0x3e, + 0x5d, 0xb0, 0x69, 0xf6, 0x0f, 0xbf, 0x54, 0x32, 0x08, 0xee, 0xf4, 0x06, 0xc3, 0x46, 0x6f, 0xa8, + 0xac, 0xf3, 0xc4, 0x61, 0xb7, 0xd1, 0x6c, 0x2b, 0x59, 0xf5, 0xe7, 0x59, 0x28, 0xc7, 0xa7, 0x6c, + 0xdf, 0xdd, 0x1b, 0x16, 0xbb, 0x99, 0xb2, 0xb2, 0x9b, 0x69, 0xc1, 0xd4, 0xe3, 0x9f, 0xc9, 0xc8, + 0x45, 0x9f, 0x4e, 0x90, 0x0d, 0xaa, 0x60, 0xf9, 0x09, 0x80, 0xfc, 0x37, 0x7c, 0x02, 0x40, 0x8e, + 0x62, 0x2f, 0xa4, 0xa3, 0xd8, 0x17, 0x3e, 0xaa, 0x50, 0xa4, 0xe7, 0xce, 0xe5, 0x8f, 0x2a, 0xd0, + 0xd7, 0x65, 0x9f, 0xd8, 0xd6, 0x99, 0x08, 0xfb, 0x16, 0xa9, 0xf4, 0x0e, 0x0d, 0x2f, 0xd9, 0xa1, + 0xbf, 0x89, 0xb4, 0x7f, 0x08, 0xdb, 0xe3, 0xd3, 0xf8, 0x91, 0xf5, 0xc4, 0xb9, 0x52, 0xa5, 0x2a, + 0xad, 0xc4, 0xb1, 0x37, 0xe2, 0x4f, 0xdc, 0xd5, 0x64, 0x37, 0x50, 0x3c, 0x5a, 0xf1, 0x37, 0xef, + 0xfe, 0xbf, 0x0c, 0x40, 0x72, 0xfe, 0xf4, 0x4b, 0xbb, 0x72, 0x25, 0x6f, 0x59, 0xf6, 0x05, 0xde, + 0xb2, 0x97, 0xbd, 0x72, 0xf7, 0x35, 0x94, 0xe3, 0x13, 0xc7, 0xef, 0x3e, 0xb1, 0xbe, 0x55, 0x91, + 0xbf, 0x1d, 0xb9, 0xb5, 0xe3, 0x23, 0xbb, 0x5f, 0xb6, 0x2f, 0x52, 0xc5, 0x67, 0x5f, 0x52, 0xfc, + 0x39, 0xf7, 0x2d, 0xc7, 0x85, 0xff, 0x8a, 0x57, 0x93, 0x3c, 0xd1, 0x73, 0xa9, 0x89, 0xae, 0xce, + 0x85, 0x83, 0xfc, 0x97, 0x2f, 0xfa, 0x5b, 0x35, 0xf8, 0xbf, 0x64, 0x22, 0x2f, 0x6e, 0xfc, 0x2e, + 0xfe, 0x73, 0x95, 0xde, 0xd5, 0x8e, 0xe8, 0x6f, 0x53, 0xdc, 0x0b, 0x7d, 0x54, 0xb9, 0x17, 0xf9, + 0xa8, 0xde, 0x80, 0x3c, 0xdf, 0xee, 0xf2, 0xcf, 0xf3, 0x4f, 0x71, 0xfc, 0x4b, 0x3f, 0x74, 0xa3, + 0xaa, 0x42, 0xc9, 0xe7, 0xed, 0xdd, 0x8e, 0xf2, 0x8d, 0x3e, 0xd2, 0x43, 0xb7, 0x6c, 0xfe, 0x2f, + 0x2e, 0x51, 0xbf, 0x6b, 0x97, 0xbc, 0xd8, 0x6d, 0xa1, 0xfe, 0xaf, 0x0c, 0xd4, 0x52, 0x11, 0x04, + 0xdf, 0xa1, 0x88, 0x95, 0x72, 0x39, 0xfb, 0x7f, 0x90, 0x5c, 0x4e, 0x85, 0x01, 0x97, 0xd2, 0x61, + 0xc0, 0x28, 0xee, 0xaa, 0x29, 0x13, 0x66, 0x95, 0xb1, 0x93, 0x59, 0x69, 0xec, 0xdc, 0x8c, 0xbf, + 0x55, 0xda, 0x69, 0xf1, 0xa8, 0xdb, 0x9a, 0x26, 0x41, 0xd8, 0xa7, 0x70, 0x4d, 0x38, 0x11, 0x78, + 0xff, 0x78, 0x63, 0x3d, 0xfe, 0x92, 0xa9, 0x30, 0xca, 0xaf, 0x70, 0x02, 0xfe, 0x99, 0xa2, 0x71, + 0x23, 0xc2, 0xaa, 0x1d, 0xa8, 0xa5, 0x42, 0x33, 0xa4, 0xaf, 0x2c, 0x67, 0xe4, 0xaf, 0x2c, 0xb3, + 0x1d, 0xc8, 0x9f, 0x9d, 0x58, 0xbe, 0xb5, 0xe2, 0x2d, 0x6b, 0x8e, 0x50, 0xbf, 0x0f, 0x55, 0x39, + 0x4c, 0x8c, 0xbd, 0x0d, 0x79, 0x3b, 0xb4, 0xa6, 0x91, 0x7b, 0xe4, 0xca, 0x72, 0x24, 0x59, 0x27, + 0xb4, 0xa6, 0x1a, 0x27, 0x52, 0xff, 0x30, 0x03, 0xca, 0x22, 0x4e, 0xfa, 0x14, 0x74, 0xe6, 0x39, + 0x9f, 0x82, 0x5e, 0x4f, 0x55, 0x72, 0xd5, 0xd7, 0x9c, 0x77, 0x22, 0xa5, 0x64, 0xc5, 0x97, 0x84, + 0x09, 0xc1, 0xee, 0x42, 0xc9, 0xb7, 0xe8, 0x3b, 0xbb, 0xe6, 0x8a, 0xdb, 0x27, 0x31, 0x4e, 0xfd, + 0xfd, 0x0c, 0x14, 0x45, 0x4c, 0xdb, 0x4a, 0x7f, 0xd5, 0x9b, 0x50, 0xe4, 0xdf, 0xdc, 0x8d, 0x1e, + 0xbb, 0x5b, 0x8a, 0xd6, 0x8d, 0xf0, 0xec, 0x26, 0x8f, 0xf4, 0x4b, 0xfb, 0xaf, 0x0e, 0x1d, 0xc3, + 0xd5, 0x08, 0x2e, 0xbe, 0x3e, 0x66, 0x4c, 0xc5, 0xeb, 0x08, 0xfc, 0x95, 0x32, 0x20, 0x10, 0x3d, + 0x84, 0xa0, 0xfe, 0x3a, 0x14, 0x45, 0xcc, 0xdc, 0xca, 0xaa, 0xbc, 0xe4, 0x1b, 0xaa, 0xea, 0x0e, + 0x40, 0x12, 0x44, 0xb7, 0x2a, 0x07, 0xf5, 0x3e, 0x94, 0xa2, 0xb8, 0x39, 0x9c, 0x7f, 0x49, 0xd1, + 0xe2, 0x5a, 0x93, 0x5c, 0x19, 0x47, 0x7c, 0x1f, 0xa2, 0xeb, 0x8d, 0x4e, 0xc9, 0x59, 0xfe, 0x2e, + 0xd0, 0x1d, 0xaf, 0xe1, 0xd2, 0x73, 0x6e, 0xe9, 0x0f, 0x89, 0xc4, 0x44, 0xec, 0x3e, 0xc4, 0xf2, + 0xf2, 0x65, 0xae, 0x05, 0xb5, 0x11, 0xdd, 0x06, 0xa4, 0x59, 0xf6, 0x48, 0x78, 0x53, 0xbb, 0xf4, + 0x9c, 0x68, 0x46, 0x7e, 0xc7, 0x39, 0x55, 0x27, 0x4d, 0x22, 0x53, 0x37, 0xa0, 0x2a, 0x07, 0xfb, + 0xa8, 0x0d, 0xd8, 0x3a, 0xb0, 0x42, 0x03, 0xe5, 0x4f, 0xf4, 0xc8, 0x15, 0x9f, 0xbf, 0xf8, 0x23, + 0x3d, 0x7f, 0x17, 0xe9, 0x34, 0x4e, 0xa4, 0xfe, 0x3c, 0x07, 0xca, 0x22, 0xee, 0x45, 0x37, 0x23, + 0x6f, 0x41, 0xc5, 0xa3, 0x79, 0x91, 0xfa, 0xbe, 0x1c, 0x07, 0x49, 0x77, 0x0a, 0x52, 0x5f, 0x0e, + 0x2a, 0xd9, 0xc1, 0x3e, 0xff, 0x76, 0xd0, 0x55, 0x7e, 0x0d, 0xce, 0xf1, 0x46, 0x34, 0xad, 0xab, + 0x74, 0xeb, 0xad, 0xeb, 0x8d, 0xe8, 0xc2, 0xa5, 0xf0, 0x4e, 0xf0, 0x08, 0xd4, 0xaa, 0x56, 0x12, + 0x2e, 0x09, 0x3a, 0xbf, 0x13, 0x37, 0x0d, 0xc2, 0x40, 0xdc, 0x23, 0x2e, 0x71, 0xc0, 0x30, 0x88, + 0xbe, 0x42, 0x30, 0x12, 0x1f, 0x43, 0xcb, 0xd2, 0x57, 0x08, 0x9a, 0x2e, 0xdd, 0xb7, 0xa4, 0xb7, + 0x99, 0x47, 0xe2, 0x33, 0x90, 0xe2, 0x3b, 0x10, 0x88, 0xba, 0xcd, 0x3f, 0x17, 0xe7, 0x5b, 0x41, + 0xc0, 0x5f, 0x95, 0x2c, 0x8b, 0xe7, 0x44, 0x05, 0x30, 0x7e, 0xb4, 0x57, 0x7c, 0xc9, 0x0f, 0x49, + 0x40, 0xbc, 0x6d, 0xc9, 0xbf, 0xe3, 0x87, 0x04, 0xd7, 0xa0, 0xf4, 0x13, 0xcf, 0xb5, 0xc8, 0xcb, + 0x51, 0xa1, 0x5a, 0x15, 0x31, 0x7d, 0x60, 0xcc, 0x70, 0x23, 0x70, 0xe8, 0xca, 0x3a, 0xbf, 0x69, + 0xc8, 0x13, 0xea, 0xbf, 0xca, 0xc0, 0xf6, 0x62, 0x5f, 0xd3, 0x34, 0xaa, 0x42, 0xa9, 0xd9, 0xef, + 0xea, 0xbd, 0xc6, 0x41, 0x5b, 0x59, 0x63, 0x9b, 0x50, 0xe9, 0xef, 0x7e, 0xd6, 0x6e, 0x0e, 0x39, + 0x20, 0x43, 0xef, 0x40, 0x0c, 0xf4, 0xfd, 0x4e, 0xab, 0xd5, 0xee, 0x71, 0x8b, 0xa2, 0xbf, 0xfb, + 0x99, 0xde, 0xed, 0x37, 0xf9, 0x67, 0xbc, 0xa2, 0x88, 0x87, 0x81, 0x92, 0xa3, 0x78, 0x08, 0x0a, + 0x86, 0xc7, 0x64, 0x9e, 0x47, 0x79, 0x3f, 0x1d, 0xe8, 0xcd, 0xde, 0x50, 0x29, 0x60, 0xaa, 0x77, + 0xd4, 0xed, 0x52, 0x8a, 0xc2, 0x39, 0x9b, 0xfd, 0x83, 0x43, 0xad, 0x3d, 0x18, 0xe8, 0x83, 0xce, + 0x8f, 0xdb, 0x4a, 0x89, 0x4a, 0xd6, 0x3a, 0x8f, 0x3b, 0x3d, 0x0e, 0x28, 0xb3, 0x22, 0x64, 0x0f, + 0x3a, 0x3d, 0xfe, 0xfe, 0xc5, 0x41, 0xe3, 0x0b, 0xa5, 0x82, 0x3f, 0x06, 0x47, 0x07, 0x4a, 0x95, + 0x95, 0x21, 0xdf, 0x6d, 0x3f, 0x69, 0x77, 0x95, 0x9a, 0xfa, 0xef, 0xb2, 0x91, 0x46, 0x4c, 0x71, + 0x4e, 0xdf, 0x44, 0x0b, 0x5c, 0x75, 0xbe, 0x18, 0x77, 0x5a, 0x56, 0xea, 0xb4, 0x6f, 0xf2, 0xe1, + 0xea, 0xdb, 0x50, 0x8b, 0x83, 0x03, 0xa4, 0x2f, 0x07, 0x54, 0x23, 0xe0, 0x8a, 0xe3, 0x8b, 0xc2, + 0x8a, 0xe3, 0x8b, 0x99, 0x1d, 0xa2, 0x95, 0x8d, 0x32, 0x97, 0xcf, 0xa4, 0x32, 0x42, 0xf8, 0xe7, + 0xe5, 0x5f, 0x01, 0x4a, 0xe8, 0x73, 0xd7, 0x8e, 0x3e, 0x2a, 0x5a, 0x42, 0xc0, 0x91, 0x6b, 0x87, + 0x8b, 0xc1, 0x09, 0xe5, 0xa5, 0xe0, 0x04, 0x79, 0x73, 0x86, 0xf4, 0xe6, 0x9c, 0xfe, 0x9e, 0x37, + 0xff, 0x9a, 0xa8, 0xf4, 0x3d, 0xef, 0xb7, 0x81, 0x8d, 0xe6, 0x3e, 0x3d, 0xb0, 0x27, 0x91, 0x55, + 0x89, 0x4c, 0x11, 0x98, 0x78, 0x57, 0x64, 0x6f, 0xc0, 0xe6, 0x02, 0x35, 0xd9, 0xd2, 0x65, 0x6d, + 0x23, 0x4d, 0xca, 0x1e, 0xc0, 0x25, 0x31, 0xb7, 0x53, 0x7d, 0x2b, 0xae, 0xaf, 0x72, 0x54, 0x23, + 0xe9, 0x61, 0xf5, 0xd7, 0xa0, 0x14, 0x85, 0xb4, 0xbd, 0x58, 0xd9, 0x5d, 0x31, 0xae, 0xea, 0xdf, + 0x5d, 0x87, 0x72, 0x1c, 0xe0, 0xf6, 0x8d, 0x66, 0x07, 0x7d, 0x22, 0x25, 0x38, 0x95, 0x45, 0x4c, + 0x09, 0x01, 0xd1, 0x48, 0x89, 0x1b, 0x5f, 0x73, 0xdf, 0x8e, 0x34, 0x36, 0x0e, 0x39, 0xf2, 0x6d, + 0x7a, 0x45, 0xc7, 0x76, 0xa5, 0x8b, 0xa6, 0x65, 0xad, 0x84, 0x00, 0x5a, 0x68, 0xd7, 0x80, 0x7e, + 0x13, 0x67, 0xf4, 0x89, 0x73, 0xdb, 0x3d, 0x45, 0xbe, 0xe7, 0x7c, 0xe2, 0x9c, 0x3e, 0xf0, 0xc2, + 0xa3, 0x6b, 0x78, 0x4c, 0x41, 0xf4, 0x95, 0xc4, 0x57, 0xa0, 0x3c, 0x8f, 0x3f, 0xb3, 0x29, 0x66, + 0xc4, 0x3c, 0xfa, 0xc8, 0x66, 0x7a, 0x54, 0xcb, 0x8b, 0xa3, 0xba, 0x38, 0xa7, 0x61, 0x69, 0x4e, + 0xab, 0x21, 0x14, 0x45, 0x90, 0xdf, 0x8b, 0x3b, 0xfc, 0x85, 0x5d, 0xa5, 0x40, 0xd6, 0x70, 0xa2, + 0xdb, 0xad, 0xf8, 0x73, 0xa1, 0x62, 0xb9, 0x85, 0x8a, 0xa9, 0x7f, 0x7d, 0x1d, 0x20, 0x09, 0x16, + 0x64, 0xef, 0x2c, 0x04, 0x26, 0x67, 0x96, 0xb6, 0xfd, 0x85, 0x78, 0xe4, 0x85, 0x67, 0xa5, 0xd6, + 0xbf, 0xc1, 0xb3, 0x52, 0x0f, 0xa1, 0x16, 0xf8, 0xa3, 0x97, 0x3a, 0xdc, 0x2b, 0x81, 0x3f, 0x8a, + 0xfd, 0xed, 0xef, 0x02, 0x26, 0xe9, 0xc9, 0xc9, 0xc4, 0x50, 0x5d, 0xd2, 0x5a, 0xca, 0x81, 0x3f, + 0xea, 0x1f, 0x7f, 0xd5, 0xe2, 0xd7, 0xec, 0xcc, 0x20, 0xd4, 0x57, 0x49, 0x89, 0x4d, 0x33, 0x08, + 0x5b, 0xb2, 0xa0, 0xb8, 0x03, 0x1b, 0x48, 0xbb, 0x24, 0x2c, 0xaa, 0x66, 0x90, 0x1c, 0xb0, 0xa8, + 0xbf, 0x17, 0x1d, 0x49, 0x2f, 0xf8, 0x72, 0xd9, 0x47, 0xc2, 0x10, 0x97, 0x94, 0x88, 0xfa, 0x2a, + 0xd7, 0x2f, 0x7f, 0x04, 0x2b, 0x26, 0x5d, 0xfe, 0x62, 0xe2, 0xfa, 0x37, 0xfd, 0x62, 0xe2, 0x0e, + 0x40, 0xf2, 0xe8, 0x27, 0xae, 0xc0, 0xf8, 0xb2, 0x4e, 0x99, 0x5f, 0xc3, 0xb9, 0x7f, 0x00, 0x97, + 0x56, 0x7c, 0x3e, 0x9e, 0x5d, 0x82, 0xcd, 0xc6, 0xe3, 0xc7, 0x7a, 0xb3, 0xdf, 0xdb, 0xeb, 0x3c, + 0xd6, 0xc5, 0xb5, 0x9c, 0xd7, 0xe0, 0x55, 0x09, 0xf8, 0x58, 0xeb, 0x1f, 0x1d, 0x62, 0xa2, 0xd9, + 0x18, 0x8a, 0x0f, 0xb8, 0x67, 0xee, 0xbf, 0x06, 0x55, 0xf9, 0xe3, 0xce, 0x74, 0xa7, 0xc7, 0x73, + 0x2d, 0xfe, 0xf1, 0x9a, 0xee, 0x4f, 0x3e, 0x50, 0x32, 0xf7, 0x55, 0xa8, 0x48, 0x9f, 0x8e, 0x42, + 0x8a, 0x7d, 0x23, 0x38, 0x11, 0x1f, 0x32, 0x31, 0xdc, 0x89, 0xa5, 0x64, 0xee, 0xdf, 0x45, 0x1d, + 0x5e, 0xfe, 0x70, 0x13, 0x40, 0xa1, 0xe7, 0xf9, 0x53, 0xc3, 0x11, 0x74, 0xd6, 0x3c, 0x40, 0xba, + 0x77, 0xe1, 0xf2, 0xca, 0xcf, 0x50, 0xd1, 0x8d, 0x30, 0x7b, 0x3a, 0x73, 0x2c, 0x7e, 0xb7, 0x69, + 0xff, 0xe2, 0xd8, 0xb7, 0x4d, 0x25, 0x73, 0xff, 0x93, 0x85, 0xcf, 0x94, 0x1c, 0xf5, 0x76, 0xfb, + 0x47, 0xbd, 0x56, 0xbb, 0xc5, 0x6f, 0x1d, 0x75, 0x7a, 0xcd, 0xee, 0xd1, 0xa0, 0xf3, 0x44, 0x6c, + 0xad, 0xed, 0x2f, 0xa2, 0xe4, 0xfa, 0xfd, 0xfd, 0xe8, 0x81, 0x88, 0xa8, 0xd6, 0xdd, 0x7e, 0xa3, + 0xc5, 0xb7, 0xe4, 0xf8, 0x89, 0xa8, 0xe1, 0x2e, 0xff, 0xbc, 0x89, 0xd6, 0x1e, 0x1c, 0x75, 0x87, + 0xd1, 0x73, 0x54, 0x1b, 0x00, 0x9d, 0x66, 0x7b, 0xb7, 0xad, 0x3d, 0x46, 0x82, 0xec, 0xfd, 0x1f, + 0x42, 0xfd, 0x79, 0xd7, 0x85, 0xb0, 0x6d, 0xcd, 0xfd, 0x06, 0x5d, 0xc9, 0xc2, 0x2d, 0xb9, 0xaf, + 0xf3, 0x14, 0x39, 0x0e, 0xb5, 0x76, 0xb7, 0x4d, 0x31, 0xb5, 0xf7, 0x7f, 0x9a, 0x91, 0xd4, 0xd3, + 0xe8, 0xca, 0x47, 0x0c, 0x10, 0x1d, 0x2e, 0x83, 0x34, 0xcb, 0x30, 0x95, 0x0c, 0xbb, 0x02, 0x2c, + 0x05, 0xea, 0x7a, 0x23, 0xc3, 0x51, 0xd6, 0x29, 0x7a, 0x36, 0x82, 0xd3, 0xf5, 0x42, 0x25, 0xcb, + 0x5e, 0x85, 0x6b, 0x31, 0xac, 0xeb, 0x9d, 0x1d, 0xfa, 0xb6, 0xe7, 0xdb, 0xe1, 0x05, 0x47, 0xe7, + 0xee, 0xff, 0xdf, 0xe2, 0xec, 0x37, 0x35, 0x69, 0xb1, 0x80, 0x86, 0x69, 0x26, 0x30, 0x92, 0x93, + 0xca, 0x1a, 0xbb, 0x0a, 0x97, 0x68, 0x93, 0x58, 0x40, 0x64, 0xd8, 0x2b, 0x70, 0x35, 0xb2, 0xa1, + 0x17, 0x91, 0xeb, 0x88, 0xd4, 0x2c, 0x8a, 0xbc, 0x5c, 0x42, 0x66, 0x77, 0x7f, 0xf0, 0x27, 0xbf, + 0xb8, 0x99, 0xf9, 0xe7, 0xbf, 0xb8, 0x99, 0xf9, 0xf7, 0xbf, 0xb8, 0xb9, 0xf6, 0x87, 0xff, 0xf1, + 0x66, 0xe6, 0xc7, 0xef, 0x4c, 0xec, 0xf0, 0x64, 0x7e, 0xfc, 0x60, 0xe4, 0x4d, 0xdf, 0x9d, 0x1a, + 0xa1, 0x6f, 0x9f, 0xf3, 0xdd, 0x2a, 0x4a, 0xb8, 0xd6, 0xbb, 0xb3, 0xd3, 0xc9, 0xbb, 0xb3, 0xe3, + 0x77, 0x71, 0xdd, 0x1c, 0x17, 0x66, 0xbe, 0x17, 0x7a, 0x8f, 0xfe, 0x77, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x3f, 0x10, 0xae, 0xd1, 0x3e, 0x8c, 0x00, 0x00, } func (m *Type) Marshal() (dAtA []byte, err error) { @@ -18936,6 +18955,21 @@ func (m *UpdateCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.TargetUpdateCtxIdx != 0 { + i = encodeVarintPlan(dAtA, i, uint64(m.TargetUpdateCtxIdx)) + i-- + dAtA[i] = 0x70 + } + if m.DedupByTargetRowId { + i-- + if m.DedupByTargetRowId { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x68 + } if m.CountDeleteAffectRows { i-- if m.CountDeleteAffectRows { @@ -28696,6 +28730,12 @@ func (m *UpdateCtx) ProtoSize() (n int) { if m.CountDeleteAffectRows { n += 2 } + if m.DedupByTargetRowId { + n += 2 + } + if m.TargetUpdateCtxIdx != 0 { + n += 1 + sovPlan(uint64(m.TargetUpdateCtxIdx)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -42558,6 +42598,45 @@ func (m *UpdateCtx) Unmarshal(dAtA []byte) error { } } m.CountDeleteAffectRows = bool(v != 0) + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DedupByTargetRowId", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DedupByTargetRowId = bool(v != 0) + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TargetUpdateCtxIdx", wireType) + } + m.TargetUpdateCtxIdx = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TargetUpdateCtxIdx |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipPlan(dAtA[iNdEx:]) diff --git a/pkg/sql/colexec/multi_update/delete.go b/pkg/sql/colexec/multi_update/delete.go index 32e1a2ac4a824..2bd1c634f3217 100644 --- a/pkg/sql/colexec/multi_update/delete.go +++ b/pkg/sql/colexec/multi_update/delete.go @@ -55,7 +55,7 @@ func (update *MultiUpdate) delete_table( for i := 0; i < rowCount; i++ { if !rowIdNulls.Contains(uint64(i)) { - for deleteIdx, inputIdx := range updateCtx.DeleteCols { + for deleteIdx, inputIdx := range updateCtx.DeleteCols[:2] { err = deleteBatch.Vecs[deleteIdx].UnionOne(inputBatch.Vecs[inputIdx], int64(i), proc.Mp()) if err != nil { return err @@ -65,7 +65,7 @@ func (update *MultiUpdate) delete_table( } } else { - for deleteIdx, inputIdx := range updateCtx.DeleteCols { + for deleteIdx, inputIdx := range updateCtx.DeleteCols[:2] { err = deleteBatch.Vecs[deleteIdx].UnionBatch(inputBatch.Vecs[inputIdx], 0, inputBatch.Vecs[inputIdx].Length(), nil, proc.GetMPool()) if err != nil { return err diff --git a/pkg/sql/colexec/multi_update/multi_update.go b/pkg/sql/colexec/multi_update/multi_update.go index 6160c7615162d..c89abe16120d0 100644 --- a/pkg/sql/colexec/multi_update/multi_update.go +++ b/pkg/sql/colexec/multi_update/multi_update.go @@ -20,7 +20,9 @@ import ( "time" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/perfcounter" "github.com/matrixorigin/matrixone/pkg/sql/colexec" @@ -376,35 +378,104 @@ func (update *MultiUpdate) updateFlushS3Info(proc *process.Process, analyzer pro } func (update *MultiUpdate) updateOneBatch(proc *process.Process, analyzer process.Analyzer, bat *batch.Batch) (err error) { + targetBatches := make(map[int]*batch.Batch) + defer func() { + for _, targetBatch := range targetBatches { + if targetBatch != bat { + targetBatch.Clean(proc.Mp()) + } + } + }() + for i, updateCtx := range update.MultiUpdateCtx { + targetIdx := updateCtx.TargetUpdateCtxIdx + if targetIdx < 0 || targetIdx >= len(update.MultiUpdateCtx) { + return moerr.NewInternalError(proc.Ctx, "invalid multi-target update context index") + } + contextBatch, ok := targetBatches[targetIdx] + if !ok { + contextBatch, _, err = filterTargetRows(proc, update.MultiUpdateCtx[targetIdx], bat) + if err != nil { + return err + } + targetBatches[targetIdx] = contextBatch + } + if contextBatch.RowCount() == 0 { + continue + } + // delete rows if len(updateCtx.DeleteCols) > 0 { - err = update.delete_table(proc, analyzer, updateCtx, bat, i) - if err != nil { - return + if err = update.delete_table(proc, analyzer, updateCtx, contextBatch, i); err != nil { + return err } } // insert rows - if len(updateCtx.InsertCols) > 0 { - tableType := update.ctr.updateCtxInfos[updateCtx.TableDef.Name].tableType - switch tableType { - case UpdateMainTable: - err = update.insert_main_table(proc, analyzer, i, bat) - case UpdateUniqueIndexTable: - err = update.insert_unique_index_table(proc, analyzer, i, bat) - case UpdateSecondaryIndexTable: - err = update.insert_secondary_index_table(proc, analyzer, i, bat) - } - if err != nil { - return - } + if len(updateCtx.InsertCols) == 0 { + continue + } + tableType := update.ctr.updateCtxInfos[updateCtx.TableDef.Name].tableType + switch tableType { + case UpdateMainTable: + err = update.insert_main_table(proc, analyzer, i, contextBatch) + case UpdateUniqueIndexTable: + err = update.insert_unique_index_table(proc, analyzer, i, contextBatch) + case UpdateSecondaryIndexTable: + err = update.insert_secondary_index_table(proc, analyzer, i, contextBatch) + } + if err != nil { + return err } } return nil } +func filterTargetRows( + proc *process.Process, + updateCtx *MultiUpdateCtx, + input *batch.Batch, +) (*batch.Batch, bool, error) { + if !updateCtx.DedupByTargetRowID { + return input, false, nil + } + if len(updateCtx.DeleteCols) < 3 || + updateCtx.DeleteCols[0] < 0 || + updateCtx.DeleteCols[0] >= len(input.Vecs) || + updateCtx.DeleteCols[2] < 0 || + updateCtx.DeleteCols[2] >= len(input.Vecs) { + return nil, false, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector columns") + } + + rowIDVec := input.Vecs[updateCtx.DeleteCols[0]] + rowNumberVec := input.Vecs[updateCtx.DeleteCols[2]] + if rowIDVec.GetType().Oid != types.T_Rowid || rowNumberVec.GetType().Oid != types.T_int64 { + return nil, false, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector types") + } + + rowNumbers := vector.MustFixedColWithTypeCheck[int64](rowNumberVec) + rowIDNulls := rowIDVec.GetNulls() + rowNumberNulls := rowNumberVec.GetNulls() + selections := make([]int64, 0, input.RowCount()) + for i := 0; i < input.RowCount(); i++ { + if rowIDNulls.Contains(uint64(i)) || + rowNumberNulls.Contains(uint64(i)) || + rowNumbers[i] != 1 { + continue + } + selections = append(selections, int64(i)) + } + + filtered, err := input.Clone(proc.Mp(), false) + if err != nil { + return nil, false, err + } + filtered.Shrink(selections, false) + filtered.SetRowCount(len(selections)) + return filtered, true, nil +} + func (update *MultiUpdate) resetMultiUpdateCtxs() { update.ctr.updateCtxInfos = make(map[string]*updateCtxInfo, len(update.MultiUpdateCtx)) diff --git a/pkg/sql/colexec/multi_update/multi_update_partition.go b/pkg/sql/colexec/multi_update/multi_update_partition.go index 247ee15bfd10f..22fd77c38cb2c 100644 --- a/pkg/sql/colexec/multi_update/multi_update_partition.go +++ b/pkg/sql/colexec/multi_update/multi_update_partition.go @@ -443,6 +443,8 @@ func (ctx *MultiUpdateCtx) clone() *MultiUpdateCtx { PartitionCols: ctx.PartitionCols, SkipInsertOnNullPk: ctx.SkipInsertOnNullPk, InsertPkColIdx: ctx.InsertPkColIdx, + DedupByTargetRowID: ctx.DedupByTargetRowID, + TargetUpdateCtxIdx: ctx.TargetUpdateCtxIdx, } objRef := *ctx.ObjRef def := *ctx.TableDef diff --git a/pkg/sql/colexec/multi_update/multi_update_partition_test.go b/pkg/sql/colexec/multi_update/multi_update_partition_test.go index 6e06f227e5190..2adb19cd52c73 100644 --- a/pkg/sql/colexec/multi_update/multi_update_partition_test.go +++ b/pkg/sql/colexec/multi_update/multi_update_partition_test.go @@ -252,11 +252,13 @@ func TestDeleteAffectedRows(t *testing.T) { // PartitionCols and keeps nested objects independent. func TestMultiUpdateCtxClonePartitionCols(t *testing.T) { original := &MultiUpdateCtx{ - InsertCols: []int{1, 2, 3}, - DeleteCols: []int{4, 5}, - PartitionCols: []int{6, 7, 8, 9}, - ObjRef: &plan.ObjectRef{SchemaName: "test", ObjName: "t1"}, - TableDef: &plan.TableDef{Name: "t1"}, + InsertCols: []int{1, 2, 3}, + DeleteCols: []int{4, 5}, + PartitionCols: []int{6, 7, 8, 9}, + DedupByTargetRowID: true, + TargetUpdateCtxIdx: 10, + ObjRef: &plan.ObjectRef{SchemaName: "test", ObjName: "t1"}, + TableDef: &plan.TableDef{Name: "t1"}, } cloned := original.clone() @@ -267,6 +269,8 @@ func TestMultiUpdateCtxClonePartitionCols(t *testing.T) { "PartitionCols should not be DeleteCols") require.Equal(t, original.InsertCols, cloned.InsertCols) require.Equal(t, original.DeleteCols, cloned.DeleteCols) + require.True(t, cloned.DedupByTargetRowID) + require.Equal(t, original.TargetUpdateCtxIdx, cloned.TargetUpdateCtxIdx) require.Equal(t, original.ObjRef.SchemaName, cloned.ObjRef.SchemaName) require.Equal(t, original.TableDef.Name, cloned.TableDef.Name) require.NotSame(t, original.ObjRef, cloned.ObjRef) diff --git a/pkg/sql/colexec/multi_update/s3writer_delegate.go b/pkg/sql/colexec/multi_update/s3writer_delegate.go index 962e43fb001fe..6f76d2aba6e2d 100644 --- a/pkg/sql/colexec/multi_update/s3writer_delegate.go +++ b/pkg/sql/colexec/multi_update/s3writer_delegate.go @@ -259,6 +259,30 @@ func (writer *s3WriterDelegate) append( } mp := proc.Mp() + targetBatches := make(map[int]*batch.Batch) + defer func() { + for _, targetBatch := range targetBatches { + if targetBatch != inBatch { + targetBatch.Clean(mp) + } + } + }() + contextBatches := make([]*batch.Batch, len(writer.updateCtxs)) + for i, updateCtx := range writer.updateCtxs { + targetIdx := updateCtx.TargetUpdateCtxIdx + if targetIdx < 0 || targetIdx >= len(writer.updateCtxs) { + return moerr.NewInternalError(proc.Ctx, "invalid multi-target update context index") + } + targetBatch, ok := targetBatches[targetIdx] + if !ok { + targetBatch, _, err = filterTargetRows(proc, writer.updateCtxs[targetIdx], inBatch) + if err != nil { + return err + } + targetBatches[targetIdx] = targetBatch + } + contextBatches[i] = targetBatch + } // Route insert columns directly to per-table sinkers (no clone). // Auto-spill S3 writes during Write use proc.Ctx; perfcounter tracking @@ -267,8 +291,12 @@ func (writer *s3WriterDelegate) append( if len(updateCtx.InsertCols) == 0 || writer.insertSinkers[i] == nil { continue } + contextBatch := contextBatches[i] + if contextBatch.RowCount() == 0 { + continue + } insertAttrs := writer.updateCtxInfos[updateCtx.TableDef.Name].insertAttrs - projBat := inBatch.SelectColumns(updateCtx.InsertCols, insertAttrs) + projBat := contextBatch.SelectColumns(updateCtx.InsertCols, insertAttrs) tableType := writer.updateCtxInfos[updateCtx.TableDef.Name].tableType @@ -300,7 +328,7 @@ func (writer *s3WriterDelegate) append( for insertIdx, inputIdx := range updateCtx.InsertCols { col := updateCtx.TableDef.Cols[insertIdx] if col.Default != nil && !col.Default.NullAbility && !strings.HasPrefix(col.Name, catalog.PrefixCBColName) { - if inBatch.Vecs[inputIdx].HasNull() { + if contextBatch.Vecs[inputIdx].HasNull() { return moerr.NewConstraintViolation(proc.Ctx, fmt.Sprintf("Column '%s' cannot be null", col.Name)) } } @@ -362,10 +390,14 @@ func (writer *s3WriterDelegate) append( if len(updateCtx.DeleteCols) == 0 { continue } + contextBatch := contextBatches[i] + if contextBatch.RowCount() == 0 { + continue + } if writer.deleteBatches[i] == nil { writer.deleteBatches[i] = batch.NewBatchSet(objectio.BlockMaxRows) } - projBat := inBatch.SelectColumns(updateCtx.DeleteCols, DeleteBatchAttrs) + projBat := contextBatch.SelectColumns(updateCtx.DeleteCols[:2], DeleteBatchAttrs) if _, err = writer.deleteBatches[i].Extend(mp, projBat, nil); err != nil { return } diff --git a/pkg/sql/colexec/multi_update/types.go b/pkg/sql/colexec/multi_update/types.go index b9dcaacb7a48b..778c19ba6e371 100644 --- a/pkg/sql/colexec/multi_update/types.go +++ b/pkg/sql/colexec/multi_update/types.go @@ -117,6 +117,11 @@ type MultiUpdateCtx struct { // InsertPkColIdx is the PK column's index within InsertCols. It is only // used with SkipInsertOnNullPk for REPLACE delete-only rows. InsertPkColIdx int + // DedupByTargetRowID makes this context consume only the whole input row + // selected for its physical target row. The planner supplies an independent + // row_number() partition for every updated target table. + DedupByTargetRowID bool + TargetUpdateCtxIdx int } func (update MultiUpdate) TypeName() string { diff --git a/pkg/sql/colexec/multi_update/update_test.go b/pkg/sql/colexec/multi_update/update_test.go index 6451b1954dd9f..81f2fae07b6de 100644 --- a/pkg/sql/colexec/multi_update/update_test.go +++ b/pkg/sql/colexec/multi_update/update_test.go @@ -64,6 +64,77 @@ func TestUpdateTableWithUniqueKey(t *testing.T) { runTestCases(t, proc, []*testCase{case1}) } +func TestFilterTargetRowsKeepsIndependentWholeRows(t *testing.T) { + proc := testutil.NewProcess(t) + mp := proc.Mp() + + bat := batch.NewWithSize(5) + bat.Vecs[0] = testutil.MakeRowIdVector( + []types.Rowid{ + types.BuildTestRowid(1, 1), + types.BuildTestRowid(1, 1), + types.BuildTestRowid(1, 2), + types.BuildTestRowid(1, 3), + }, + []uint64{3}, + mp, + ) + bat.Vecs[1] = testutil.NewInt64Vector( + 4, + types.T_int64.ToType(), + mp, + false, + nil, + []int64{1, 2, 1, 1}, + ) + bat.Vecs[2] = testutil.MakeRowIdVector( + []types.Rowid{ + types.BuildTestRowid(2, 1), + types.BuildTestRowid(2, 2), + types.BuildTestRowid(2, 2), + types.BuildTestRowid(2, 3), + }, + nil, + mp, + ) + bat.Vecs[3] = testutil.NewInt64Vector( + 4, + types.T_int64.ToType(), + mp, + false, + nil, + []int64{1, 1, 2, 1}, + ) + bat.Vecs[4] = testutil.NewInt32Vector( + 4, + types.T_int32.ToType(), + mp, + false, + nil, + []int32{10, 20, 30, 40}, + ) + bat.SetRowCount(4) + defer bat.Clean(mp) + + first, clean, err := filterTargetRows(proc, &MultiUpdateCtx{ + DedupByTargetRowID: true, + DeleteCols: []int{0, 4, 1}, + }, bat) + require.NoError(t, err) + require.True(t, clean) + defer first.Clean(mp) + require.Equal(t, []int32{10, 30}, vector.MustFixedColWithTypeCheck[int32](first.Vecs[4])) + + second, clean, err := filterTargetRows(proc, &MultiUpdateCtx{ + DedupByTargetRowID: true, + DeleteCols: []int{2, 4, 3}, + }, bat) + require.NoError(t, err) + require.True(t, clean) + defer second.Clean(mp) + require.Equal(t, []int32{10, 20, 40}, vector.MustFixedColWithTypeCheck[int32](second.Vecs[4])) +} + // update table s3 func TestUpdateS3SingleTable(t *testing.T) { hasUniqueKey := false diff --git a/pkg/sql/compile/operator.go b/pkg/sql/compile/operator.go index 1d0feb0d13dd6..abac685d0e446 100644 --- a/pkg/sql/compile/operator.go +++ b/pkg/sql/compile/operator.go @@ -897,6 +897,8 @@ func constructMultiUpdate( PartitionCols: partitionCols, SkipInsertOnNullPk: updateCtx.SkipInsertOnNullPk, InsertPkColIdx: int(updateCtx.InsertPkColIdx), + DedupByTargetRowID: updateCtx.DedupByTargetRowId, + TargetUpdateCtxIdx: int(updateCtx.TargetUpdateCtxIdx), } } arg.Action = action diff --git a/pkg/sql/compile/remoterun.go b/pkg/sql/compile/remoterun.go index 0f176a434e989..f94fa7eed84a4 100644 --- a/pkg/sql/compile/remoterun.go +++ b/pkg/sql/compile/remoterun.go @@ -872,6 +872,8 @@ func convertToPipelineInstruction(op vm.Operator, proc *process.Process, ctx *sc SkipInsertOnNullPk: muCtx.SkipInsertOnNullPk, InsertPkColIdx: int32(muCtx.InsertPkColIdx), CountDeleteAffectRows: t.CountDeleteAffectRows, + DedupByTargetRowId: muCtx.DedupByTargetRowID, + TargetUpdateCtxIdx: int32(muCtx.TargetUpdateCtxIdx), } updateCtxList[i].InsertCols = make([]plan.ColRef, len(muCtx.InsertCols)) @@ -1383,6 +1385,8 @@ func convertToVmOperator(opr *pipeline.Instruction, ctx *scopeContext, eng engin TableDef: muCtx.TableDef, SkipInsertOnNullPk: muCtx.SkipInsertOnNullPk, InsertPkColIdx: int(muCtx.InsertPkColIdx), + DedupByTargetRowID: muCtx.DedupByTargetRowId, + TargetUpdateCtxIdx: int(muCtx.TargetUpdateCtxIdx), } arg.MultiUpdateCtx[i].InsertCols = make([]int, len(muCtx.InsertCols)) diff --git a/pkg/sql/compile/remoterun_test.go b/pkg/sql/compile/remoterun_test.go index 1150381e91c78..a177023d1ef44 100644 --- a/pkg/sql/compile/remoterun_test.go +++ b/pkg/sql/compile/remoterun_test.go @@ -655,12 +655,14 @@ func Test_DMLOperatorSerializationRoundtrip(t *testing.T) { op := &multi_update.MultiUpdate{ MultiUpdateCtx: []*multi_update.MultiUpdateCtx{ { - ObjRef: &plan.ObjectRef{ObjName: "t1"}, - TableDef: &plan.TableDef{Name: "t1"}, - InsertCols: []int{0, 1, 2}, - DeleteCols: []int{3, 4}, - PartitionCols: []int{5, 6}, - InsertPkColIdx: 1, + ObjRef: &plan.ObjectRef{ObjName: "t1"}, + TableDef: &plan.TableDef{Name: "t1"}, + InsertCols: []int{0, 1, 2}, + DeleteCols: []int{3, 4, 8}, + PartitionCols: []int{5, 6}, + InsertPkColIdx: 1, + DedupByTargetRowID: true, + TargetUpdateCtxIdx: 0, }, }, Action: multi_update.UpdateWriteTable, @@ -674,8 +676,10 @@ func Test_DMLOperatorSerializationRoundtrip(t *testing.T) { restoredOp := restored.(*multi_update.MultiUpdate) require.Equal(t, []int{5, 6}, restoredOp.MultiUpdateCtx[0].PartitionCols) require.Equal(t, []int{0, 1, 2}, restoredOp.MultiUpdateCtx[0].InsertCols) - require.Equal(t, []int{3, 4}, restoredOp.MultiUpdateCtx[0].DeleteCols) + require.Equal(t, []int{3, 4, 8}, restoredOp.MultiUpdateCtx[0].DeleteCols) require.Equal(t, 1, restoredOp.MultiUpdateCtx[0].InsertPkColIdx) + require.True(t, restoredOp.MultiUpdateCtx[0].DedupByTargetRowID) + require.Equal(t, 0, restoredOp.MultiUpdateCtx[0].TargetUpdateCtxIdx) require.True(t, restoredOp.IsRemote) require.False(t, restoredOp.CountDeleteAffectRows, "CountDeleteAffectRows must stay false when the source op did not set it") diff --git a/pkg/sql/plan/bind_update.go b/pkg/sql/plan/bind_update.go index 962e606ff5834..aeb9de50aafc4 100644 --- a/pkg/sql/plan/bind_update.go +++ b/pkg/sql/plan/bind_update.go @@ -16,6 +16,7 @@ package plan import ( "fmt" + "sort" "strings" "github.com/matrixorigin/matrixone/pkg/catalog" @@ -284,6 +285,17 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) selectNode := builder.qry.Nodes[lastNodeID] selectNodeTag := selectNode.BindingTags[0] + updatedTargetCount := 0 + for i := range dmlCtx.aliases { + if len(dmlCtx.updateCol2Expr[i]) > 0 { + updatedTargetCount++ + } + } + isMultiTargetUpdate := updatedTargetCount > 1 + targetRowNumberPos := make([]int32, len(dmlCtx.aliases)) + for i := range targetRowNumberPos { + targetRowNumberPos[i] = -1 + } for i, alias := range dmlCtx.aliases { if len(dmlCtx.updateCol2Expr[i]) == 0 { @@ -383,7 +395,32 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) } - if stmt.From != nil && len(stmt.From.Tables) > 0 { + if isMultiTargetUpdate { + for i, alias := range dmlCtx.aliases { + if len(dmlCtx.updateCol2Expr[i]) == 0 { + continue + } + rowIDPos, ok := oldColName2Idx[alias+"."+catalog.Row_ID] + if !ok { + return 0, moerr.NewInternalErrorf( + builder.GetContext(), + "bind update err, can not find row_id for target %s", + alias, + ) + } + lastNodeID, selectNode, selectNodeTag, targetRowNumberPos[i], err = + builder.appendTargetRowNumberNode( + bindCtx, + lastNodeID, + selectNode, + selectNodeTag, + rowIDPos, + ) + if err != nil { + return 0, err + } + } + } else if stmt.From != nil && len(stmt.From.Tables) > 0 { lastNodeID, selectNode, selectNodeTag, err = builder.appendUpdateFromDedupNode( bindCtx, lastNodeID, selectNode, selectNodeTag, dmlCtx, oldColName2Idx, newColName2Idx) if err != nil { @@ -504,6 +541,41 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) } return builder.makeIndexPartExprFromInputExpr(inputExpr, partName, prefixLengths) } + guardTargetDedupExpr := func(targetIdx int, expr *plan.Expr) (*plan.Expr, error) { + rowNumberExpr := &plan.Expr{ + Typ: selectNode.ProjectList[targetRowNumberPos[targetIdx]].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: selectNodeTag, + ColPos: targetRowNumberPos[targetIdx], + }, + }, + } + isSelectedExpr, err := BindFuncExprImplByPlanExpr( + builder.GetContext(), + "=", + []*plan.Expr{rowNumberExpr, MakePlan2Int64ConstExprWithType(1)}, + ) + if err != nil { + return nil, err + } + nullExpr := &plan.Expr{ + Typ: expr.Typ, + Expr: &plan.Expr_Lit{ + Lit: &plan.Literal{Isnull: true}, + }, + } + guardedExpr, err := BindFuncExprImplByPlanExpr( + builder.GetContext(), + "if", + []*plan.Expr{isSelectedExpr, expr, nullExpr}, + ) + if err != nil { + return nil, err + } + return guardedExpr, nil + } + dedupKeyPos := make(map[string]int32) for i, tableDef := range dmlCtx.tableDefs { if len(dmlCtx.updateCol2Expr[i]) == 0 { @@ -535,7 +607,30 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) } newPkExpr, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", args) + if isMultiTargetUpdate { + newPkExpr, err = guardTargetDedupExpr(i, newPkExpr) + if err != nil { + return 0, err + } + dedupKeyPos[alias+"."+tableDef.Pkey.PkeyColName] = + int32(len(newProjNode.ProjectList)) + } newProjNode.ProjectList = append(newProjNode.ProjectList, newPkExpr) + } else if isMultiTargetUpdate { + key := alias + "." + tableDef.Pkey.PkeyColName + colPos := newColName2Idx[key] + pkExpr := &plan.Expr{ + Typ: selectNode.ProjectList[colPos].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: selectNodeTag, ColPos: colPos}, + }, + } + pkExpr, err = guardTargetDedupExpr(i, pkExpr) + if err != nil { + return 0, err + } + dedupKeyPos[key] = int32(len(newProjNode.ProjectList)) + newProjNode.ProjectList = append(newProjNode.ProjectList, pkExpr) } scanTag := builder.genNewBindTag() @@ -559,16 +654,17 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) }, } + key := alias + "." + tableDef.Pkey.PkeyColName + newPkPos := newColName2Idx[key] + if pos, ok := dedupKeyPos[key]; ok { + newPkPos = pos + } rightExpr := &plan.Expr{ Typ: pkTyp, Expr: &plan.Expr_Col{ - Col: &plan.ColRef{ - RelPos: newProjTag, - ColPos: newColName2Idx[alias+"."+tableDef.Pkey.PkeyColName], - }, + Col: &plan.ColRef{RelPos: newProjTag, ColPos: newPkPos}, }, } - joinCond, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*plan.Expr{ leftExpr, rightExpr, @@ -675,6 +771,14 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) } newUkExpr, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "serial", newArgs) + if isMultiTargetUpdate { + newUkExpr, err = guardTargetDedupExpr(i, newUkExpr) + if err != nil { + return 0, err + } + dedupKeyPos[idxTableDef.Name+"."+catalog.IndexTableIndexColName] = + int32(len(newProjNode.ProjectList)) + } newProjNode.ProjectList = append(newProjNode.ProjectList, newUkExpr) } else { partName := catalog.ResolveAlias(idxDef.Parts[0]) @@ -699,10 +803,34 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) if err != nil { return 0, err } + if isMultiTargetUpdate { + newPartExpr, err = guardTargetDedupExpr(i, newPartExpr) + if err != nil { + return 0, err + } + dedupKeyPos[idxTableDef.Name+"."+catalog.IndexTableIndexColName] = + int32(len(newProjNode.ProjectList)) + } newProjNode.ProjectList = append(newProjNode.ProjectList, newPartExpr) } else { oldColName2Idx[idxTableDef.Name+"."+catalog.IndexTableIndexColName] = oldColName2Idx[alias+"."+partName] newColName2Idx[idxTableDef.Name+"."+catalog.IndexTableIndexColName] = newColName2Idx[alias+"."+partName] + if isMultiTargetUpdate { + key := idxTableDef.Name + "." + catalog.IndexTableIndexColName + colPos := newColName2Idx[key] + newPartExpr := &plan.Expr{ + Typ: selectNode.ProjectList[colPos].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: selectNodeTag, ColPos: colPos}, + }, + } + newPartExpr, err = guardTargetDedupExpr(i, newPartExpr) + if err != nil { + return 0, err + } + dedupKeyPos[key] = int32(len(newProjNode.ProjectList)) + newProjNode.ProjectList = append(newProjNode.ProjectList, newPartExpr) + } } } @@ -719,16 +847,17 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) }, } + key := idxTableDef.Name + "." + catalog.IndexTableIndexColName + newIdxPos := newColName2Idx[key] + if pos, ok := dedupKeyPos[key]; ok { + newIdxPos = pos + } rightExpr := &plan.Expr{ Typ: pkTyp, Expr: &plan.Expr_Col{ - Col: &plan.ColRef{ - RelPos: newProjTag, - ColPos: newColName2Idx[idxTableDef.Name+"."+catalog.IndexTableIndexColName], - }, + Col: &plan.ColRef{RelPos: newProjTag, ColPos: newIdxPos}, }, } - joinCond, _ := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*plan.Expr{ leftExpr, rightExpr, @@ -876,7 +1005,7 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) }) joinType := plan.Node_LEFT - if !idxDef.Unique && !isSpatialIndexDef(idxDef) { + if !isMultiTargetUpdate && !idxDef.Unique && !isSpatialIndexDef(idxDef) { joinType = plan.Node_INNER } lastNodeID = builder.appendNode(&plan.Node{ @@ -894,6 +1023,8 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) finalProjTag := builder.genNewBindTag() finalColName2Idx := make(map[string]int32) var finalProjList []*plan.Expr + targetRowNumberFinalPos := make([]int32, len(dmlCtx.aliases)) + targetUpdateCtxIdx := make([]int32, len(dmlCtx.aliases)) finalProjNode := &plan.Node{ NodeType: plan.Node_PROJECT, @@ -966,8 +1097,21 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) }, }) } + if isMultiTargetUpdate { + targetRowNumberFinalPos[i] = int32(len(finalProjList)) + rowNumberPos := targetRowNumberPos[i] + finalProjList = append(finalProjList, &plan.Expr{ + Typ: selectNode.ProjectList[rowNumberPos].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: selectNodeTag, + ColPos: rowNumberPos, + }, + }, + }) + } - updateCtxList = append(updateCtxList, &plan.UpdateCtx{ + updateCtx := &plan.UpdateCtx{ ObjRef: dmlCtx.objRefs[i], TableDef: tableDef, InsertCols: insertCols, @@ -981,7 +1125,17 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) ColPos: oldPkPos, }, }, - }) + } + if isMultiTargetUpdate { + updateCtx.DedupByTargetRowId = true + targetUpdateCtxIdx[i] = int32(len(updateCtxList)) + updateCtx.TargetUpdateCtxIdx = targetUpdateCtxIdx[i] + updateCtx.DeleteCols = append(updateCtx.DeleteCols, plan.ColRef{ + RelPos: finalProjTag, + ColPos: targetRowNumberFinalPos[i], + }) + } + updateCtxList = append(updateCtxList, updateCtx) lockTargets = append(lockTargets, &plan.LockTarget{ TableId: tableDef.TblId, @@ -1126,12 +1280,16 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) insertCols[1].RelPos = finalProjTag insertCols[1].ColPos = finalColName2Idx[alias+"."+tableDef.Pkey.PkeyColName] - updateCtxList = append(updateCtxList, &plan.UpdateCtx{ + updateCtx := &plan.UpdateCtx{ ObjRef: idxNode.ObjRef, TableDef: idxNode.TableDef, InsertCols: insertCols, DeleteCols: deleteCols, - }) + } + if isMultiTargetUpdate { + updateCtx.TargetUpdateCtxIdx = targetUpdateCtxIdx[i] + } + updateCtxList = append(updateCtxList, updateCtx) if idxDef.Unique { lockTargets = append(lockTargets, &plan.LockTarget{ @@ -1155,6 +1313,12 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) } finalProjNode.ProjectList = finalProjList + sort.SliceStable(lockTargets, func(i, j int) bool { + if lockTargets[i].TableId != lockTargets[j].TableId { + return lockTargets[i].TableId < lockTargets[j].TableId + } + return lockTargets[i].PrimaryColIdxInBat < lockTargets[j].PrimaryColIdxInBat + }) dmlNode := &plan.Node{ NodeType: plan.Node_MULTI_UPDATE, @@ -1215,6 +1379,108 @@ func primaryKeyUpdated(tableDef *plan.TableDef, updateCols map[string]tree.Expr) return false } +// appendTargetRowNumberNode appends an independent row_number() window for one +// updated target. Unlike appendRowNumberDedupNode, it deliberately keeps every +// input row: each MULTI_UPDATE context later consumes only its own row_number=1 +// rows. This is required for multi-target UPDATE because one shared global +// filter cannot independently choose representatives for two different Rowids. +func (builder *QueryBuilder) appendTargetRowNumberNode( + bindCtx *BindContext, + lastNodeID int32, + selectNode *plan.Node, + selectNodeTag int32, + targetRowIDPos int32, +) (int32, *plan.Node, int32, int32, error) { + childColExpr := func(pos int32) *plan.Expr { + e := selectNode.ProjectList[pos] + name := "" + if col, ok := e.Expr.(*plan.Expr_Col); ok { + name = col.Col.Name + } + return &plan.Expr{ + Typ: e.Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: selectNodeTag, + ColPos: pos, + Name: name, + }, + }, + } + } + + targetRowIDExpr := childColExpr(targetRowIDPos) + partitionBy := []*plan.OrderBySpec{{ + Expr: targetRowIDExpr, + Flag: plan.OrderBySpec_INTERNAL, + }} + windowTag := builder.genNewBindTag() + lastNodeID = builder.appendNode(&plan.Node{ + NodeType: plan.Node_PARTITION, + Children: []int32{lastNodeID}, + OrderBy: partitionBy, + BindingTags: []int32{windowTag}, + }, bindCtx) + + rowNumberFunc, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "row_number", nil) + if err != nil { + return 0, nil, 0, 0, err + } + rowNumberExpr := &plan.Expr{ + Typ: rowNumberFunc.Typ, + Expr: &plan.Expr_W{ + W: &plan.WindowSpec{ + WindowFunc: rowNumberFunc, + Name: "row_number", + PartitionBy: []*plan.Expr{targetRowIDExpr}, + Frame: &plan.FrameClause{ + Type: plan.FrameClause_ROWS, + Start: &plan.FrameBound{ + Type: plan.FrameBound_PRECEDING, + UnBounded: true, + }, + End: &plan.FrameBound{ + Type: plan.FrameBound_FOLLOWING, + UnBounded: true, + }, + }, + }, + }, + } + lastNodeID = builder.appendNode(&plan.Node{ + NodeType: plan.Node_WINDOW, + Children: []int32{lastNodeID}, + WinSpecList: []*plan.Expr{rowNumberExpr}, + WindowIdx: 0, + BindingTags: []int32{windowTag}, + }, bindCtx) + + rowNumberProjectPos := int32(len(selectNode.ProjectList)) + projectTag := builder.genNewBindTag() + projectList := make([]*plan.Expr, 0, len(selectNode.ProjectList)+1) + for pos := range selectNode.ProjectList { + projectList = append(projectList, childColExpr(int32(pos))) + } + projectList = append(projectList, &plan.Expr{ + Typ: rowNumberFunc.Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: windowTag, + ColPos: 0, + Name: "__mo_multi_target_update_row_number", + }, + }, + }) + projectNode := &plan.Node{ + NodeType: plan.Node_PROJECT, + Children: []int32{lastNodeID}, + ProjectList: projectList, + BindingTags: []int32{projectTag}, + } + lastNodeID = builder.appendNode(projectNode, bindCtx) + return lastNodeID, projectNode, projectTag, rowNumberProjectPos, nil +} + func (builder *QueryBuilder) appendUpdateFromDedupNode( bindCtx *BindContext, lastNodeID int32, diff --git a/pkg/sql/plan/build_test.go b/pkg/sql/plan/build_test.go index 67e78516dd71b..c2259735ddb6a 100644 --- a/pkg/sql/plan/build_test.go +++ b/pkg/sql/plan/build_test.go @@ -2117,6 +2117,73 @@ func TestUpdatePgStyleFromDedupsDuplicateSourceMatchesOnNewPath(t *testing.T) { } } +func TestMultiTargetUpdateUsesIndependentModernSelectors(t *testing.T) { + mock := NewMockOptimizer(true) + logicPlan, err := runOneStmt( + mock, + t, + "UPDATE nation n JOIN nation2 n2 ON n.n_nationkey = n2.n_nationkey "+ + "SET n.n_name = n2.n_name, n2.n_comment = n.n_comment", + ) + require.NoError(t, err) + + query := logicPlan.GetQuery() + var multiUpdate *plan.Node + rowNumberWindows := 0 + for _, node := range query.Nodes { + if node.NodeType == plan.Node_WINDOW { + for _, specExpr := range node.WinSpecList { + if spec := specExpr.GetW(); spec != nil && + spec.Name == "row_number" && + len(spec.PartitionBy) == 1 { + rowNumberWindows++ + } + } + } + if node.NodeType == plan.Node_MULTI_UPDATE { + multiUpdate = node + } + } + require.NotNil(t, multiUpdate) + require.Equal(t, 2, rowNumberWindows) + + mainCtxs := make(map[string]*plan.UpdateCtx) + for _, updateCtx := range multiUpdate.UpdateCtxList { + if updateCtx.TableDef == nil { + continue + } + if updateCtx.TableDef.Name == "nation" || updateCtx.TableDef.Name == "nation2" { + mainCtxs[updateCtx.TableDef.Name] = updateCtx + } + } + require.Len(t, mainCtxs, 2) + for _, name := range []string{"nation", "nation2"} { + updateCtx := mainCtxs[name] + require.NotNil(t, updateCtx) + require.True(t, updateCtx.DedupByTargetRowId) + require.Len(t, updateCtx.DeleteCols, 3) + require.NotEqual(t, updateCtx.DeleteCols[0].ColPos, updateCtx.DeleteCols[2].ColPos) + } + require.NotEqual( + t, + mainCtxs["nation"].DeleteCols[0].ColPos, + mainCtxs["nation2"].DeleteCols[0].ColPos, + ) + + require.Len(t, multiUpdate.Children, 1) + lockNode := query.Nodes[multiUpdate.Children[0]] + require.Equal(t, plan.Node_LOCK_OP, lockNode.NodeType) + for i := 1; i < len(lockNode.LockTargets); i++ { + previous := lockNode.LockTargets[i-1] + current := lockNode.LockTargets[i] + require.True(t, + previous.TableId < current.TableId || + (previous.TableId == current.TableId && + previous.PrimaryColIdxInBat <= current.PrimaryColIdxInBat), + ) + } +} + func TestUpdatePgStyleFromDedupPicksWholeSourceRow(t *testing.T) { mock := NewMockOptimizer(true) diff --git a/pkg/sql/plan/deepcopy.go b/pkg/sql/plan/deepcopy.go index be38153c8510f..ad18bd71b4da9 100644 --- a/pkg/sql/plan/deepcopy.go +++ b/pkg/sql/plan/deepcopy.go @@ -106,6 +106,8 @@ func DeepCopyUpdateCtxList(updateCtxList []*plan.UpdateCtx) []*plan.UpdateCtx { SkipInsertOnNullPk: ctx.SkipInsertOnNullPk, InsertPkColIdx: ctx.InsertPkColIdx, CountDeleteAffectRows: ctx.CountDeleteAffectRows, + DedupByTargetRowId: ctx.DedupByTargetRowId, + TargetUpdateCtxIdx: ctx.TargetUpdateCtxIdx, } } diff --git a/pkg/sql/plan/dml_context.go b/pkg/sql/plan/dml_context.go index ae3941b3cebd2..2e7fcaa6783b8 100644 --- a/pkg/sql/plan/dml_context.go +++ b/pkg/sql/plan/dml_context.go @@ -114,13 +114,6 @@ func (dmlCtx *DMLContext) ResolveUpdateTables(ctx CompilerContext, stmt *tree.Up } } - if len(usedTbl) > 1 { - return newLegacyUpdatePlannerRouteError( - updateRouteReasonMultiTarget, - moerr.NewUnsupportedDML(ctx.GetContext(), "multi-table update"), - ) - } - dmlCtx.updateCol2Expr = make([]map[string]tree.Expr, len(dmlCtx.tableDefs)) for alias, columnMap := range usedTbl { idx := dmlCtx.aliasMap[alias] @@ -249,17 +242,27 @@ func (dmlCtx *DMLContext) ResolveSingleTable(ctx CompilerContext, tbl tree.Table } } - //if joinTbl, ok := tbl.(*tree.JoinTableExpr); ok { - // dmlCtx.needAggFilter = true - // err := setTableExprToDmlTableInfo(ctx, joinTbl.Left, dmlCtx, aliasMap, withMap) - // if err != nil { - // return err - // } - // if joinTbl.Right != nil { - // return setTableExprToDmlTableInfo(ctx, joinTbl.Right, dmlCtx, aliasMap, withMap) - // } - // return nil - //} + if joinTbl, ok := tbl.(*tree.JoinTableExpr); ok { + if err := dmlCtx.ResolveSingleTable( + ctx, + joinTbl.Left, + aliasMap, + withMap, + respectFKCheck, + ); err != nil { + return err + } + if joinTbl.Right != nil { + return dmlCtx.ResolveSingleTable( + ctx, + joinTbl.Right, + aliasMap, + withMap, + respectFKCheck, + ) + } + return nil + } if baseTbl, ok := tbl.(*tree.TableName); ok { dbName = string(baseTbl.SchemaName) diff --git a/pkg/sql/plan/update_planner_route_test.go b/pkg/sql/plan/update_planner_route_test.go index f717e12c62dfc..04a02d9746040 100644 --- a/pkg/sql/plan/update_planner_route_test.go +++ b/pkg/sql/plan/update_planner_route_test.go @@ -171,12 +171,6 @@ func TestBindUpdateProducesTypedPlannerRoutes(t *testing.T) { wantRoute updatePlannerRoute wantReason updatePlannerRouteReason }{ - { - name: "multi target", - sql: "UPDATE emp, dept SET emp.sal = 1, dept.loc = 'x'", - wantRoute: updatePlannerLegacy, - wantReason: updateRouteReasonMultiTarget, - }, { name: "foreign key metadata", sql: "UPDATE nation SET n_name = 'x'", diff --git a/proto/plan.proto b/proto/plan.proto index d709a5383fb66..6af520970ab7f 100644 --- a/proto/plan.proto +++ b/proto/plan.proto @@ -633,6 +633,8 @@ message UpdateCtx { bool skip_insert_on_null_pk = 10; int32 insert_pk_col_idx = 11; bool count_delete_affect_rows = 12; + bool dedup_by_target_row_id = 13; + int32 target_update_ctx_idx = 14; } message InsertCtx { diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.result b/test/distributed/cases/dml/update/update_multi_target_modern.result new file mode 100644 index 0000000000000..ed951e2813dcb --- /dev/null +++ b/test/distributed/cases/dml/update/update_multi_target_modern.result @@ -0,0 +1,67 @@ +DROP TABLE IF EXISTS multi_update_target_a; +DROP TABLE IF EXISTS multi_update_target_b; +CREATE TABLE multi_update_target_a ( +id INT PRIMARY KEY, +grp INT, +x INT, +y INT, +UNIQUE KEY ux_a (id, x), +KEY iy_a (y) +); +CREATE TABLE multi_update_target_b ( +id INT PRIMARY KEY, +grp INT, +x INT, +y INT, +UNIQUE KEY ux_b (id, x), +KEY iy_b (y) +); +INSERT INTO multi_update_target_a VALUES +(1, 1, 0, 0), +(2, 1, 0, 0), +(3, 2, 0, 0); +INSERT INTO multi_update_target_b VALUES +(10, 1, 0, 0), +(20, 1, 0, 0), +(30, 3, 0, 0); +UPDATE multi_update_target_a a +JOIN multi_update_target_b b ON a.grp = b.grp +SET +a.x = b.id, +a.y = b.id, +b.x = a.id, +b.y = a.id; +SELECT COUNT(*) FROM multi_update_target_a WHERE x <> y; +count(*) +0 +SELECT COUNT(*) FROM multi_update_target_b WHERE x <> y; +count(*) +0 +SELECT COUNT(*) FROM multi_update_target_a WHERE grp = 1 AND x = 0; +count(*) +0 +SELECT COUNT(*) FROM multi_update_target_b WHERE grp = 1 AND x = 0; +count(*) +0 +UPDATE multi_update_target_a a +LEFT JOIN multi_update_target_b b ON a.grp = b.grp +SET +a.x = 7, +b.x = 9; +UPDATE multi_update_target_a a +RIGHT JOIN multi_update_target_b b ON a.grp = b.grp +SET +a.y = 11, +b.y = 13; +SELECT id, x, y FROM multi_update_target_a ORDER BY id; +id x y +1 7 11 +2 7 11 +3 7 0 +SELECT id, x, y FROM multi_update_target_b ORDER BY id; +id x y +10 9 13 +20 9 13 +30 0 13 +DROP TABLE multi_update_target_a; +DROP TABLE multi_update_target_b; diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.sql b/test/distributed/cases/dml/update/update_multi_target_modern.sql new file mode 100644 index 0000000000000..712140ca0f7c7 --- /dev/null +++ b/test/distributed/cases/dml/update/update_multi_target_modern.sql @@ -0,0 +1,67 @@ +-- @suit + +-- @case +-- @desc: multi-target UPDATE uses independent physical-row selection +-- @label: bvt + +DROP TABLE IF EXISTS multi_update_target_a; +DROP TABLE IF EXISTS multi_update_target_b; + +CREATE TABLE multi_update_target_a ( + id INT PRIMARY KEY, + grp INT, + x INT, + y INT, + UNIQUE KEY ux_a (id, x), + KEY iy_a (y) +); + +CREATE TABLE multi_update_target_b ( + id INT PRIMARY KEY, + grp INT, + x INT, + y INT, + UNIQUE KEY ux_b (id, x), + KEY iy_b (y) +); + +INSERT INTO multi_update_target_a VALUES + (1, 1, 0, 0), + (2, 1, 0, 0), + (3, 2, 0, 0); + +INSERT INTO multi_update_target_b VALUES + (10, 1, 0, 0), + (20, 1, 0, 0), + (30, 3, 0, 0); + +UPDATE multi_update_target_a a +JOIN multi_update_target_b b ON a.grp = b.grp +SET + a.x = b.id, + a.y = b.id, + b.x = a.id, + b.y = a.id; + +SELECT COUNT(*) FROM multi_update_target_a WHERE x <> y; +SELECT COUNT(*) FROM multi_update_target_b WHERE x <> y; +SELECT COUNT(*) FROM multi_update_target_a WHERE grp = 1 AND x = 0; +SELECT COUNT(*) FROM multi_update_target_b WHERE grp = 1 AND x = 0; + +UPDATE multi_update_target_a a +LEFT JOIN multi_update_target_b b ON a.grp = b.grp +SET + a.x = 7, + b.x = 9; + +UPDATE multi_update_target_a a +RIGHT JOIN multi_update_target_b b ON a.grp = b.grp +SET + a.y = 11, + b.y = 13; + +SELECT id, x, y FROM multi_update_target_a ORDER BY id; +SELECT id, x, y FROM multi_update_target_b ORDER BY id; + +DROP TABLE multi_update_target_a; +DROP TABLE multi_update_target_b; From d8978eb0f2d6ce51cd035fa0b50ee43fecf56a07 Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Thu, 30 Jul 2026 23:00:47 +0800 Subject: [PATCH 2/8] fix(plan): address multi-target update review findings --- pkg/sql/colexec/multi_update/multi_update.go | 24 ++- .../colexec/multi_update/s3writer_delegate.go | 41 ++++- pkg/sql/colexec/multi_update/types.go | 4 + pkg/sql/colexec/multi_update/update_test.go | 94 +++++++++- pkg/sql/plan/bind_update.go | 164 ++++++++++++++++++ pkg/sql/plan/build_test.go | 55 ++++++ pkg/sql/plan/update_planner_route_test.go | 11 ++ .../update/update_multi_target_modern.result | 69 ++++++++ .../dml/update/update_multi_target_modern.sql | 62 +++++++ 9 files changed, 512 insertions(+), 12 deletions(-) diff --git a/pkg/sql/colexec/multi_update/multi_update.go b/pkg/sql/colexec/multi_update/multi_update.go index c89abe16120d0..8c4b8004b90c2 100644 --- a/pkg/sql/colexec/multi_update/multi_update.go +++ b/pkg/sql/colexec/multi_update/multi_update.go @@ -106,6 +106,7 @@ func (update *MultiUpdate) Prepare(proc *process.Process) error { update.ctr.affectedRows = 0 update.ctr.flushed = false + update.ctr.seenTargetRows = make(map[uint64]map[types.Rowid]struct{}) update.getFlushableS3WriterFunc = update.getFlushableS3Writer update.getS3WriterFunc = update.getS3Writer update.addAffectedRowsFunc = update.doAddAffectedRows @@ -394,7 +395,12 @@ func (update *MultiUpdate) updateOneBatch(proc *process.Process, analyzer proces } contextBatch, ok := targetBatches[targetIdx] if !ok { - contextBatch, _, err = filterTargetRows(proc, update.MultiUpdateCtx[targetIdx], bat) + contextBatch, _, err = filterTargetRows( + proc, + update.MultiUpdateCtx[targetIdx], + bat, + update.ctr.seenTargetRows, + ) if err != nil { return err } @@ -436,6 +442,7 @@ func filterTargetRows( proc *process.Process, updateCtx *MultiUpdateCtx, input *batch.Batch, + seenTargetRows map[uint64]map[types.Rowid]struct{}, ) (*batch.Batch, bool, error) { if !updateCtx.DedupByTargetRowID { return input, false, nil @@ -455,8 +462,17 @@ func filterTargetRows( } rowNumbers := vector.MustFixedColWithTypeCheck[int64](rowNumberVec) + rowIDs := vector.MustFixedColWithTypeCheck[types.Rowid](rowIDVec) rowIDNulls := rowIDVec.GetNulls() rowNumberNulls := rowNumberVec.GetNulls() + var seenRows map[types.Rowid]struct{} + if seenTargetRows != nil { + seenRows = seenTargetRows[updateCtx.TableDef.TblId] + if seenRows == nil { + seenRows = make(map[types.Rowid]struct{}) + seenTargetRows[updateCtx.TableDef.TblId] = seenRows + } + } selections := make([]int64, 0, input.RowCount()) for i := 0; i < input.RowCount(); i++ { if rowIDNulls.Contains(uint64(i)) || @@ -464,6 +480,12 @@ func filterTargetRows( rowNumbers[i] != 1 { continue } + if seenRows != nil { + if _, ok := seenRows[rowIDs[i]]; ok { + continue + } + seenRows[rowIDs[i]] = struct{}{} + } selections = append(selections, int64(i)) } diff --git a/pkg/sql/colexec/multi_update/s3writer_delegate.go b/pkg/sql/colexec/multi_update/s3writer_delegate.go index 6f76d2aba6e2d..2d155e5f13de2 100644 --- a/pkg/sql/colexec/multi_update/s3writer_delegate.go +++ b/pkg/sql/colexec/multi_update/s3writer_delegate.go @@ -112,8 +112,9 @@ type s3WriterDelegate struct { batchSize uint64 flushThreshold uint64 - checkSizeCols []int - buf bytes.Buffer + checkSizeCols []int + buf bytes.Buffer + seenTargetRows map[uint64]map[types.Rowid]struct{} memController struct { grantedSize int64 @@ -144,6 +145,7 @@ func newS3Writer( insertFreeLists: make([]*containers.BatchFreeList, tableCount), isRemote: update.IsRemote, rejectZeroTemporal: update.RejectZeroTemporal, + seenTargetRows: make(map[uint64]map[types.Rowid]struct{}), } for i := range writer.insertFreeLists { writer.insertFreeLists[i] = containers.NewBatchFreeList(nil, nil, true) @@ -157,9 +159,10 @@ func newS3Writer( faultInjected := false - mainIdx := 0 + mainIdx := -1 for i, updateCtx := range update.MultiUpdateCtx { - if update.ctr.updateCtxInfos[updateCtx.TableDef.Name].tableType == UpdateMainTable { + if mainIdx == -1 && + update.ctr.updateCtxInfos[updateCtx.TableDef.Name].tableType == UpdateMainTable { mainIdx = i } @@ -173,6 +176,9 @@ func newS3Writer( } writer.updateCtxs = update.MultiUpdateCtx + if mainIdx == -1 { + return nil, moerr.NewInternalErrorNoCtx("multi update has no main table context") + } threshold := InsertWriteS3Threshold if faultInjected { @@ -184,23 +190,34 @@ func newS3Writer( //update writer.action = actionUpdate writer.flushThreshold = threshold - writer.checkSizeCols = append(writer.checkSizeCols, upCtx.InsertCols...) - writer.checkSizeCols = append(writer.checkSizeCols, upCtx.DeleteCols...) } else if len(upCtx.InsertCols) > 0 { //insert writer.action = actionInsert writer.flushThreshold = threshold - writer.checkSizeCols = append(writer.checkSizeCols, upCtx.InsertCols...) } else { //delete writer.action = actionDelete writer.flushThreshold = DeleteWriteS3Threshold - writer.checkSizeCols = append(writer.checkSizeCols, upCtx.DeleteCols...) } + writer.checkSizeCols = retainedS3InputCols(writer.updateCtxs, writer.action) return writer, nil } +func retainedS3InputCols(updateCtxs []*MultiUpdateCtx, action actionType) []int { + var cols []int + for _, updateCtx := range updateCtxs { + if action != actionDelete { + cols = append(cols, updateCtx.InsertCols...) + } + if action != actionInsert && len(updateCtx.DeleteCols) > 0 { + deleteColCount := min(2, len(updateCtx.DeleteCols)) + cols = append(cols, updateCtx.DeleteCols[:deleteColCount]...) + } + } + return cols +} + // ensureInsertSinkers lazily creates persistent per-table insert sinkers // on first call. Requires proc for Mp() and fileservice. func (writer *s3WriterDelegate) ensureInsertSinkers(proc *process.Process) error { @@ -275,7 +292,12 @@ func (writer *s3WriterDelegate) append( } targetBatch, ok := targetBatches[targetIdx] if !ok { - targetBatch, _, err = filterTargetRows(proc, writer.updateCtxs[targetIdx], inBatch) + targetBatch, _, err = filterTargetRows( + proc, + writer.updateCtxs[targetIdx], + inBatch, + writer.seenTargetRows, + ) if err != nil { return err } @@ -859,6 +881,7 @@ func (writer *s3WriterDelegate) reset(proc *process.Process) (err error) { writer.outputBat.CleanOnlyData() } + clear(writer.seenTargetRows) writer.buf.Reset() return } diff --git a/pkg/sql/colexec/multi_update/types.go b/pkg/sql/colexec/multi_update/types.go index 778c19ba6e371..982829b470201 100644 --- a/pkg/sql/colexec/multi_update/types.go +++ b/pkg/sql/colexec/multi_update/types.go @@ -17,6 +17,7 @@ package multi_update import ( "github.com/matrixorigin/matrixone/pkg/common/reuse" "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/sql/plan" "github.com/matrixorigin/matrixone/pkg/vm" "github.com/matrixorigin/matrixone/pkg/vm/engine" @@ -105,6 +106,8 @@ type container struct { insertBuf []*batch.Batch deleteBuf []*batch.Batch + + seenTargetRows map[uint64]map[types.Rowid]struct{} } type MultiUpdateCtx struct { @@ -164,6 +167,7 @@ func (update *MultiUpdate) Reset(proc *process.Process, pipelineFailed bool, err if update.ctr.s3Writer != nil { update.ctr.s3Writer.reset(proc) } + clear(update.ctr.seenTargetRows) update.ctr.state = vm.Build } diff --git a/pkg/sql/colexec/multi_update/update_test.go b/pkg/sql/colexec/multi_update/update_test.go index 81f2fae07b6de..483f287776255 100644 --- a/pkg/sql/colexec/multi_update/update_test.go +++ b/pkg/sql/colexec/multi_update/update_test.go @@ -117,24 +117,114 @@ func TestFilterTargetRowsKeepsIndependentWholeRows(t *testing.T) { defer bat.Clean(mp) first, clean, err := filterTargetRows(proc, &MultiUpdateCtx{ + TableDef: &plan.TableDef{TblId: 1}, DedupByTargetRowID: true, DeleteCols: []int{0, 4, 1}, - }, bat) + }, bat, nil) require.NoError(t, err) require.True(t, clean) defer first.Clean(mp) require.Equal(t, []int32{10, 30}, vector.MustFixedColWithTypeCheck[int32](first.Vecs[4])) second, clean, err := filterTargetRows(proc, &MultiUpdateCtx{ + TableDef: &plan.TableDef{TblId: 2}, DedupByTargetRowID: true, DeleteCols: []int{2, 4, 3}, - }, bat) + }, bat, nil) require.NoError(t, err) require.True(t, clean) defer second.Clean(mp) require.Equal(t, []int32{10, 20, 40}, vector.MustFixedColWithTypeCheck[int32](second.Vecs[4])) } +func TestFilterTargetRowsDedupsPhysicalRowsAcrossContextsAndBatches(t *testing.T) { + proc := testutil.NewProcess(t) + mp := proc.Mp() + rowID := types.BuildTestRowid(1, 1) + makeBatch := func(value int32) *batch.Batch { + bat := batch.NewWithSize(3) + bat.Vecs[0] = testutil.MakeRowIdVector([]types.Rowid{rowID}, nil, mp) + bat.Vecs[1] = testutil.NewInt64Vector( + 1, + types.T_int64.ToType(), + mp, + false, + nil, + []int64{1}, + ) + bat.Vecs[2] = testutil.NewInt32Vector( + 1, + types.T_int32.ToType(), + mp, + false, + nil, + []int32{value}, + ) + bat.SetRowCount(1) + return bat + } + updateCtx := &MultiUpdateCtx{ + TableDef: &plan.TableDef{TblId: 42}, + DedupByTargetRowID: true, + DeleteCols: []int{0, 2, 1}, + } + seen := make(map[uint64]map[types.Rowid]struct{}) + + firstInput := makeBatch(10) + defer firstInput.Clean(mp) + first, clean, err := filterTargetRows(proc, updateCtx, firstInput, seen) + require.NoError(t, err) + require.True(t, clean) + defer first.Clean(mp) + require.Equal(t, 1, first.RowCount()) + + secondInput := makeBatch(20) + defer secondInput.Clean(mp) + secondCtx := &MultiUpdateCtx{ + TableDef: &plan.TableDef{TblId: 42}, + DedupByTargetRowID: true, + DeleteCols: []int{0, 2, 1}, + } + second, clean, err := filterTargetRows(proc, secondCtx, secondInput, seen) + require.NoError(t, err) + require.True(t, clean) + defer second.Clean(mp) + require.Equal(t, 0, second.RowCount()) +} + +func TestRetainedS3InputColsCountsEveryContextCopy(t *testing.T) { + updateCtxs := []*MultiUpdateCtx{ + { + InsertCols: []int{0, 1, 2}, + DeleteCols: []int{3, 4, 9}, + }, + { + InsertCols: []int{0, 5}, + DeleteCols: []int{6, 7}, + }, + { + InsertCols: []int{8, 2}, + DeleteCols: []int{10, 11}, + }, + } + + require.Equal( + t, + []int{0, 1, 2, 3, 4, 0, 5, 6, 7, 8, 2, 10, 11}, + retainedS3InputCols(updateCtxs, actionUpdate), + ) + require.Equal( + t, + []int{0, 1, 2, 0, 5, 8, 2}, + retainedS3InputCols(updateCtxs, actionInsert), + ) + require.Equal( + t, + []int{3, 4, 6, 7, 10, 11}, + retainedS3InputCols(updateCtxs, actionDelete), + ) +} + // update table s3 func TestUpdateS3SingleTable(t *testing.T) { hasUniqueKey := false diff --git a/pkg/sql/plan/bind_update.go b/pkg/sql/plan/bind_update.go index 5ba070aa0436b..01068281c7757 100644 --- a/pkg/sql/plan/bind_update.go +++ b/pkg/sql/plan/bind_update.go @@ -15,6 +15,7 @@ package plan import ( + "context" "fmt" "sort" "strings" @@ -23,6 +24,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/features" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" planutil "github.com/matrixorigin/matrixone/pkg/sql/util" ) @@ -79,6 +81,9 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) if err != nil { return 0, err } + if err = validateModernMultiTargetUpdate(builder.GetContext(), dmlCtx); err != nil { + return 0, err + } if err = validateUpdateTargetSubqueries(builder.compCtx, stmt, dmlCtx.objRefs, dmlCtx.tableDefs); err != nil { return 0, err } @@ -288,6 +293,18 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) selectNode := builder.qry.Nodes[lastNodeID] selectNodeTag := selectNode.BindingTags[0] + lastNodeID, selectNode, selectNodeTag, err = builder.mergeSamePhysicalTargetAssignments( + bindCtx, + lastNodeID, + selectNode, + selectNodeTag, + dmlCtx, + oldColName2Idx, + newColName2Idx, + ) + if err != nil { + return 0, err + } updatedTargetCount := 0 for i := range dmlCtx.aliases { if len(dmlCtx.updateCol2Expr[i]) > 0 { @@ -1344,6 +1361,153 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) return lastNodeID, err } +func validateModernMultiTargetUpdate(ctx context.Context, dmlCtx *DMLContext) error { + updatedTargetCount := 0 + hasPartitionedTarget := false + + for i, updateCols := range dmlCtx.updateCol2Expr { + if len(updateCols) == 0 { + continue + } + updatedTargetCount++ + tableDef := dmlCtx.tableDefs[i] + hasPartitionedTarget = hasPartitionedTarget || features.IsPartitioned(tableDef.FeatureFlag) + } + + if updatedTargetCount > 1 && hasPartitionedTarget { + return newLegacyUpdatePlannerRouteError( + updateRouteReasonMultiTarget, + moerr.NewUnsupportedDML(ctx, "multi-target update with partitioned table"), + ) + } + return nil +} + +func (builder *QueryBuilder) mergeSamePhysicalTargetAssignments( + bindCtx *BindContext, + lastNodeID int32, + selectNode *plan.Node, + selectNodeTag int32, + dmlCtx *DMLContext, + oldColName2Idx map[string]int32, + newColName2Idx map[string]int32, +) (int32, *plan.Node, int32, error) { + targetsByTableID := make(map[uint64][]int) + for i, updateCols := range dmlCtx.updateCol2Expr { + if len(updateCols) > 0 { + targetsByTableID[dmlCtx.tableDefs[i].TblId] = + append(targetsByTableID[dmlCtx.tableDefs[i].TblId], i) + } + } + + hasRepeatedPhysicalTarget := false + for _, targets := range targetsByTableID { + if len(targets) > 1 { + hasRepeatedPhysicalTarget = true + break + } + } + if !hasRepeatedPhysicalTarget { + return lastNodeID, selectNode, selectNodeTag, nil + } + + childColExpr := func(pos int32) *plan.Expr { + return &plan.Expr{ + Typ: selectNode.ProjectList[pos].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: selectNodeTag, + ColPos: pos, + }, + }, + } + } + projectList := make([]*plan.Expr, len(selectNode.ProjectList)) + for i := range projectList { + projectList[i] = childColExpr(int32(i)) + } + + processedTableIDs := make(map[uint64]struct{}) + for targetIdx := range dmlCtx.aliases { + tableID := dmlCtx.tableDefs[targetIdx].TblId + if _, ok := processedTableIDs[tableID]; ok { + continue + } + processedTableIDs[tableID] = struct{}{} + targets := targetsByTableID[tableID] + if len(targets) < 2 { + continue + } + for _, targetIdx := range targets { + targetAlias := dmlCtx.aliases[targetIdx] + targetRowIDPos := oldColName2Idx[targetAlias+"."+catalog.Row_ID] + + for _, col := range dmlCtx.tableDefs[targetIdx].Cols { + var sourceTargets []int + for _, sourceIdx := range targets { + if _, ok := dmlCtx.updateCol2Expr[sourceIdx][col.Name]; ok { + sourceTargets = append(sourceTargets, sourceIdx) + } + } + if len(sourceTargets) == 0 { + continue + } + + key := targetAlias + "." + col.Name + targetOldPos := oldColName2Idx[key] + currentPos, targetUpdatesCol := newColName2Idx[key] + if !targetUpdatesCol { + currentPos = targetOldPos + oldColName2Idx[key] = int32(len(projectList)) + projectList = append(projectList, childColExpr(currentPos)) + newColName2Idx[key] = currentPos + } + + finalExpr := childColExpr(targetOldPos) + for _, sourceIdx := range sourceTargets { + sourceAlias := dmlCtx.aliases[sourceIdx] + sourceRowIDPos := oldColName2Idx[sourceAlias+"."+catalog.Row_ID] + sameRowExpr, err := BindFuncExprImplByPlanExpr( + builder.GetContext(), + "=", + []*plan.Expr{ + childColExpr(targetRowIDPos), + childColExpr(sourceRowIDPos), + }, + ) + if err != nil { + return 0, nil, 0, err + } + sourceValuePos := newColName2Idx[sourceAlias+"."+col.Name] + finalExpr, err = BindFuncExprImplByPlanExpr( + builder.GetContext(), + "if", + []*plan.Expr{ + sameRowExpr, + childColExpr(sourceValuePos), + finalExpr, + }, + ) + if err != nil { + return 0, nil, 0, err + } + } + projectList[currentPos] = finalExpr + } + } + } + + projectTag := builder.genNewBindTag() + projectNode := &plan.Node{ + NodeType: plan.Node_PROJECT, + Children: []int32{lastNodeID}, + ProjectList: projectList, + BindingTags: []int32{projectTag}, + } + lastNodeID = builder.appendNode(projectNode, bindCtx) + return lastNodeID, projectNode, projectTag, nil +} + func collectIrregularIndexUpdateCols(tableDef *plan.TableDef) (bool, map[string]bool) { irregularIndexCols := make(map[string]bool) if tableDef == nil { diff --git a/pkg/sql/plan/build_test.go b/pkg/sql/plan/build_test.go index d627c82db3217..32d3a54c56645 100644 --- a/pkg/sql/plan/build_test.go +++ b/pkg/sql/plan/build_test.go @@ -2257,6 +2257,61 @@ func TestMultiTargetUpdateUsesIndependentModernSelectors(t *testing.T) { } } +func TestSamePhysicalTargetAliasesShareMergedFinalRows(t *testing.T) { + mock := NewMockOptimizer(true) + logicPlan, err := runOneStmt( + mock, + t, + "UPDATE nation a JOIN nation b ON a.n_nationkey = b.n_nationkey "+ + "SET a.n_name = 'a', b.n_comment = 'b'", + ) + require.NoError(t, err) + + query := logicPlan.GetQuery() + var multiUpdate *plan.Node + mainContexts := 0 + mergedAssignments := 0 + var walkExpr func(*plan.Expr) + walkExpr = func(expr *plan.Expr) { + if expr == nil { + return + } + if fn := expr.GetF(); fn != nil { + if fn.GetFunc().GetObjName() == "if" { + mergedAssignments++ + } + for _, arg := range fn.Args { + walkExpr(arg) + } + } + } + for _, node := range query.Nodes { + if node.NodeType == plan.Node_MULTI_UPDATE { + multiUpdate = node + } + for _, expr := range node.ProjectList { + walkExpr(expr) + } + } + + require.NotNil(t, multiUpdate) + var tableID uint64 + for _, updateCtx := range multiUpdate.UpdateCtxList { + if updateCtx.TableDef == nil || updateCtx.TableDef.Name != "nation" { + continue + } + mainContexts++ + require.True(t, updateCtx.DedupByTargetRowId) + if tableID == 0 { + tableID = updateCtx.TableDef.TblId + } else { + require.Equal(t, tableID, updateCtx.TableDef.TblId) + } + } + require.Equal(t, 2, mainContexts) + require.GreaterOrEqual(t, mergedAssignments, 2) +} + func TestUpdatePgStyleFromDedupPicksWholeSourceRow(t *testing.T) { mock := NewMockOptimizer(true) diff --git a/pkg/sql/plan/update_planner_route_test.go b/pkg/sql/plan/update_planner_route_test.go index 04a02d9746040..487ce743bf558 100644 --- a/pkg/sql/plan/update_planner_route_test.go +++ b/pkg/sql/plan/update_planner_route_test.go @@ -24,6 +24,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/features" "github.com/matrixorigin/matrixone/pkg/sql/parsers" "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" @@ -171,6 +172,16 @@ func TestBindUpdateProducesTypedPlannerRoutes(t *testing.T) { wantRoute updatePlannerRoute wantReason updatePlannerRouteReason }{ + { + name: "multi target with partitioned table", + sql: "UPDATE nation n JOIN nation2 n2 ON n.n_nationkey = n2.n_nationkey " + + "SET n.n_name = 'a', n2.n_comment = 'b'", + prepare: func(mock *MockOptimizer) { + mock.ctxt.tables["nation"].FeatureFlag |= features.Partitioned + }, + wantRoute: updatePlannerLegacy, + wantReason: updateRouteReasonMultiTarget, + }, { name: "foreign key metadata", sql: "UPDATE nation SET n_name = 'x'", diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.result b/test/distributed/cases/dml/update/update_multi_target_modern.result index ed951e2813dcb..035f4ba615354 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.result +++ b/test/distributed/cases/dml/update/update_multi_target_modern.result @@ -65,3 +65,72 @@ id x y 30 0 13 DROP TABLE multi_update_target_a; DROP TABLE multi_update_target_b; +DROP TABLE IF EXISTS multi_update_alias_target; +CREATE TABLE multi_update_alias_target ( +id INT PRIMARY KEY, +x INT, +y INT +); +INSERT INTO multi_update_alias_target VALUES (1, 0, 0), (2, 0, 0); +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id = b.id +SET +a.x = 1, +b.y = 2; +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; +id x y +1 1 2 +2 1 2 +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id = 1 AND b.id = 1 +SET +a.x = 3, +b.y = 4; +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; +id x y +1 3 4 +2 1 2 +DROP TABLE multi_update_alias_target; +DROP TABLE IF EXISTS multi_update_partition_target; +DROP TABLE IF EXISTS multi_update_plain_target; +CREATE TABLE multi_update_partition_target ( +id INT PRIMARY KEY, +x INT +) PARTITION BY RANGE (id) ( +PARTITION p0 VALUES LESS THAN (2), +PARTITION p1 VALUES LESS THAN (MAXVALUE) +); +CREATE TABLE multi_update_plain_target ( +id INT PRIMARY KEY, +x INT +); +INSERT INTO multi_update_partition_target VALUES (1, 0), (2, 0); +INSERT INTO multi_update_plain_target VALUES (1, 0), (2, 0); +UPDATE multi_update_partition_target p +JOIN multi_update_plain_target n ON p.id = n.id +SET +p.x = p.x + 1, +n.x = n.x + 2; +SELECT id, x FROM multi_update_partition_target ORDER BY id; +id x +1 1 +2 1 +SELECT id, x FROM multi_update_plain_target ORDER BY id; +id x +1 2 +2 2 +UPDATE multi_update_plain_target n +JOIN multi_update_partition_target p ON n.id = p.id +SET +n.x = n.x + 2, +p.x = p.x + 1; +SELECT id, x FROM multi_update_partition_target ORDER BY id; +id x +1 2 +2 2 +SELECT id, x FROM multi_update_plain_target ORDER BY id; +id x +1 4 +2 4 +DROP TABLE multi_update_partition_target; +DROP TABLE multi_update_plain_target; diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.sql b/test/distributed/cases/dml/update/update_multi_target_modern.sql index 712140ca0f7c7..2fb28e7ea891c 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.sql +++ b/test/distributed/cases/dml/update/update_multi_target_modern.sql @@ -65,3 +65,65 @@ SELECT id, x, y FROM multi_update_target_b ORDER BY id; DROP TABLE multi_update_target_a; DROP TABLE multi_update_target_b; + +DROP TABLE IF EXISTS multi_update_alias_target; +CREATE TABLE multi_update_alias_target ( + id INT PRIMARY KEY, + x INT, + y INT +); +INSERT INTO multi_update_alias_target VALUES (1, 0, 0), (2, 0, 0); + +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id = b.id +SET + a.x = 1, + b.y = 2; + +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; + +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id = 1 AND b.id = 1 +SET + a.x = 3, + b.y = 4; + +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; +DROP TABLE multi_update_alias_target; + +DROP TABLE IF EXISTS multi_update_partition_target; +DROP TABLE IF EXISTS multi_update_plain_target; +CREATE TABLE multi_update_partition_target ( + id INT PRIMARY KEY, + x INT +) PARTITION BY RANGE (id) ( + PARTITION p0 VALUES LESS THAN (2), + PARTITION p1 VALUES LESS THAN (MAXVALUE) +); +CREATE TABLE multi_update_plain_target ( + id INT PRIMARY KEY, + x INT +); +INSERT INTO multi_update_partition_target VALUES (1, 0), (2, 0); +INSERT INTO multi_update_plain_target VALUES (1, 0), (2, 0); + +UPDATE multi_update_partition_target p +JOIN multi_update_plain_target n ON p.id = n.id +SET + p.x = p.x + 1, + n.x = n.x + 2; + +SELECT id, x FROM multi_update_partition_target ORDER BY id; +SELECT id, x FROM multi_update_plain_target ORDER BY id; + +UPDATE multi_update_plain_target n +JOIN multi_update_partition_target p ON n.id = p.id +SET + n.x = n.x + 2, + p.x = p.x + 1; + +SELECT id, x FROM multi_update_partition_target ORDER BY id; +SELECT id, x FROM multi_update_plain_target ORDER BY id; + +DROP TABLE multi_update_partition_target; +DROP TABLE multi_update_plain_target; From 8e44fd512a857daf079565fcc9f6c85dca00313d Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Fri, 31 Jul 2026 11:54:45 +0800 Subject: [PATCH 3/8] fix(plan): harden modern multi-target updates --- pkg/sql/colexec/multi_update/multi_update.go | 145 +++++-- .../multi_update/multi_update_partition.go | 364 ++++++++++-------- .../multi_update_partition_test.go | 54 ++- .../colexec/multi_update/s3writer_delegate.go | 33 +- pkg/sql/colexec/multi_update/types.go | 26 +- pkg/sql/colexec/multi_update/update_test.go | 92 +++-- pkg/sql/compile/operator.go | 21 +- pkg/sql/compile/operator_test.go | 16 +- pkg/sql/compile/remoterun.go | 1 + pkg/sql/plan/bind_update.go | 27 -- pkg/sql/plan/build_test.go | 24 ++ pkg/sql/plan/update_planner_route_test.go | 11 - .../update/update_multi_target_modern.result | 3 + .../dml/update/update_multi_target_modern.sql | 1 + 14 files changed, 548 insertions(+), 270 deletions(-) diff --git a/pkg/sql/colexec/multi_update/multi_update.go b/pkg/sql/colexec/multi_update/multi_update.go index 9f7c830546353..ebddce7cde9fd 100644 --- a/pkg/sql/colexec/multi_update/multi_update.go +++ b/pkg/sql/colexec/multi_update/multi_update.go @@ -20,7 +20,10 @@ import ( "time" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/hashmap" "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/rscthrottler" + "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" @@ -103,10 +106,17 @@ func (update *MultiUpdate) Prepare(proc *process.Process) error { if len(update.ctr.deleteBuf) == 0 { update.ctr.deleteBuf = make([]*batch.Batch, len(update.MultiUpdateCtx)) } + if err := update.prepareSeenTargetRows(proc); err != nil { + return err + } + if update.Action == UpdateWriteS3 { + if err := update.prepareSeenTargetRowsAdmission(proc.GetService()); err != nil { + return err + } + } update.ctr.affectedRows = 0 update.ctr.flushed = false - update.ctr.seenTargetRows = make(map[uint64]map[types.Rowid]struct{}) update.getFlushableS3WriterFunc = update.getFlushableS3Writer update.getS3WriterFunc = update.getS3Writer update.addAffectedRowsFunc = update.doAddAffectedRows @@ -395,15 +405,17 @@ func (update *MultiUpdate) updateOneBatch(proc *process.Process, analyzer proces } contextBatch, ok := targetBatches[targetIdx] if !ok { - contextBatch, _, err = filterTargetRows( + var duplicateRows uint64 + contextBatch, _, duplicateRows, err = filterTargetRows( proc, update.MultiUpdateCtx[targetIdx], bat, - update.ctr.seenTargetRows, + update.ctr.seenTargetRows[targetTableID(update.MultiUpdateCtx[targetIdx])], ) if err != nil { return err } + update.addAffectedRowsFunc(duplicateRows) targetBatches[targetIdx] = contextBatch } if contextBatch.RowCount() == 0 { @@ -442,37 +454,28 @@ func filterTargetRows( proc *process.Process, updateCtx *MultiUpdateCtx, input *batch.Batch, - seenTargetRows map[uint64]map[types.Rowid]struct{}, -) (*batch.Batch, bool, error) { + seen *hashmap.StrHashMap, +) (*batch.Batch, bool, uint64, error) { if !updateCtx.DedupByTargetRowID { - return input, false, nil + return input, false, 0, nil } if len(updateCtx.DeleteCols) < 3 || updateCtx.DeleteCols[0] < 0 || updateCtx.DeleteCols[0] >= len(input.Vecs) || updateCtx.DeleteCols[2] < 0 || updateCtx.DeleteCols[2] >= len(input.Vecs) { - return nil, false, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector columns") + return nil, false, 0, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector columns") } rowIDVec := input.Vecs[updateCtx.DeleteCols[0]] rowNumberVec := input.Vecs[updateCtx.DeleteCols[2]] if rowIDVec.GetType().Oid != types.T_Rowid || rowNumberVec.GetType().Oid != types.T_int64 { - return nil, false, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector types") + return nil, false, 0, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector types") } rowNumbers := vector.MustFixedColWithTypeCheck[int64](rowNumberVec) - rowIDs := vector.MustFixedColWithTypeCheck[types.Rowid](rowIDVec) rowIDNulls := rowIDVec.GetNulls() rowNumberNulls := rowNumberVec.GetNulls() - var seenRows map[types.Rowid]struct{} - if seenTargetRows != nil { - seenRows = seenTargetRows[updateCtx.TableDef.TblId] - if seenRows == nil { - seenRows = make(map[types.Rowid]struct{}) - seenTargetRows[updateCtx.TableDef.TblId] = seenRows - } - } selections := make([]int64, 0, input.RowCount()) for i := 0; i < input.RowCount(); i++ { if rowIDNulls.Contains(uint64(i)) || @@ -480,22 +483,114 @@ func filterTargetRows( rowNumbers[i] != 1 { continue } - if seenRows != nil { - if _, ok := seenRows[rowIDs[i]]; ok { - continue - } - seenRows[rowIDs[i]] = struct{}{} - } selections = append(selections, int64(i)) } filtered, err := input.Clone(proc.Mp(), false) if err != nil { - return nil, false, err + return nil, false, 0, err } filtered.Shrink(selections, false) filtered.SetRowCount(len(selections)) - return filtered, true, nil + if seen == nil || filtered.RowCount() == 0 { + return filtered, true, 0, nil + } + + physicalSelections := make([]int64, 0, filtered.RowCount()) + iterator := seen.NewIterator() + for offset := 0; offset < filtered.RowCount(); offset += hashmap.UnitLimit { + count := min(hashmap.UnitLimit, filtered.RowCount()-offset) + oldGroupCount := seen.GroupCount() + values, zValues, err := iterator.Insert( + offset, + count, + []*vector.Vector{filtered.Vecs[updateCtx.DeleteCols[0]]}, + ) + if err != nil { + filtered.Clean(proc.Mp()) + return nil, false, 0, err + } + for i, value := range values { + if zValues[i] != 0 && value > oldGroupCount { + physicalSelections = append(physicalSelections, int64(offset+i)) + } + } + } + duplicateRows := uint64(filtered.RowCount() - len(physicalSelections)) + filtered.Shrink(physicalSelections, false) + filtered.SetRowCount(len(physicalSelections)) + return filtered, true, duplicateRows, nil +} + +func (update *MultiUpdate) prepareSeenTargetRows(proc *process.Process) error { + if update.ctr.seenTargetRows != nil { + return nil + } + targetCounts := make(map[uint64]int) + for _, ctx := range update.MultiUpdateCtx { + if !features.IsIndexTable(ctx.TableDef.FeatureFlag) { + targetCounts[targetTableID(ctx)]++ + } + } + update.ctr.seenTargetRows = make(map[uint64]*hashmap.StrHashMap) + for tableID, count := range targetCounts { + if count < 2 { + continue + } + seen, err := hashmap.NewStrHashMap(false, proc.Mp()) + if err != nil { + return err + } + update.ctr.seenTargetRows[tableID] = seen + } + return nil +} + +func (update *MultiUpdate) prepareSeenTargetRowsAdmission(sid string) error { + if len(update.ctr.seenTargetRows) == 0 { + return nil + } + if update.ctr.seenRowsRSC != nil { + return nil + } + value, ok := runtime.ServiceRuntime(sid).GetGlobalVariables(runtime.CNMemoryThrottler) + if !ok { + return moerr.NewInternalErrorNoCtxf("can not get global variable %s", runtime.CNMemoryThrottler) + } + update.ctr.seenRowsRSC = value.(rscthrottler.RSCThrottler) + if err := update.admitSeenTargetRowsGrowth(update.seenTargetRowsSize()); err != nil { + update.ctr.seenRowsRSC = nil + return err + } + return nil +} + +func (update *MultiUpdate) admitSeenTargetRowsGrowth(increment int64) error { + if increment <= 0 || update.ctr.seenRowsRSC == nil { + return nil + } + if _, granted := update.ctr.seenRowsRSC.Acquire(increment); !granted { + return moerr.NewInternalErrorNoCtx( + "multi-target update Rowid deduplication exceeded the CN memory admission limit", + ) + } + update.ctr.seenRowsGrant += increment + return nil +} + +func (update *MultiUpdate) seenTargetRowsSize() int64 { + var size int64 + for _, seen := range update.ctr.seenTargetRows { + size += seen.Size() + } + return size +} + +func targetTableID(ctx *MultiUpdateCtx) uint64 { + if ctx.TargetTableID != 0 { + return ctx.TargetTableID + } + return ctx.TableDef.TblId } func (update *MultiUpdate) resetMultiUpdateCtxs() { diff --git a/pkg/sql/colexec/multi_update/multi_update_partition.go b/pkg/sql/colexec/multi_update/multi_update_partition.go index 22fd77c38cb2c..56d073505e05c 100644 --- a/pkg/sql/colexec/multi_update/multi_update_partition.go +++ b/pkg/sql/colexec/multi_update/multi_update_partition.go @@ -29,29 +29,33 @@ import ( type PartitionMultiUpdate struct { vm.OperatorBase - raw *MultiUpdate - affectedRows uint64 + raw *MultiUpdate + rawContexts []*MultiUpdateCtx + targets []*partitionUpdateTarget + affectedRows uint64 + writers map[uint64]*s3WriterDelegate + freeWriters []*s3WriterDelegate + nextWriterID uint64 +} + +type partitionUpdateTarget struct { + contexts []*MultiUpdateCtx tableID uint64 meta partition.PartitionMetadata mainIndexes []uint64 partitionIndexes map[uint64][]engine.Relation - rawTableIDs []uint64 - rawTableFlags []uint64 - writers map[uint64]*s3WriterDelegate - freeWriters []*s3WriterDelegate + writerIDs map[uint64]uint64 } func NewPartitionMultiUpdate( raw *MultiUpdate, - tableID uint64, ) vm.Operator { if raw.Action == UpdateFlushS3Info { return raw } return &PartitionMultiUpdate{ - raw: raw, - tableID: tableID, + raw: raw, } } @@ -65,7 +69,7 @@ func NewPartitionMultiUpdateFrom( op.CountDeleteAffectRows = from.raw.CountDeleteAffectRows op.RejectZeroTemporal = from.raw.RejectZeroTemporal op.Engine = from.raw.Engine - return NewPartitionMultiUpdate(op, from.tableID) + return NewPartitionMultiUpdate(op) } func (op *PartitionMultiUpdate) String(buf *bytes.Buffer) { @@ -86,46 +90,80 @@ func (op *PartitionMultiUpdate) Prepare( op.OpAnalyzer.Reset() } - var err error - op.meta, _, err = proc.GetPartitionService().GetStorage().GetMetadata( - proc.Ctx, - op.tableID, - proc.GetTxnOperator(), - ) - if err != nil { - return err - } - _, _, r, err := op.raw.Engine.GetRelationById( - proc.Ctx, - proc.GetTxnOperator(), - op.tableID, - ) - if err != nil { + op.raw.OperatorBase = op.OperatorBase + if err := op.raw.Prepare(proc); err != nil { return err } - if len(r.GetExtraInfo().IndexTables) > 0 { - op.mainIndexes = r.GetExtraInfo().IndexTables - op.partitionIndexes = make(map[uint64][]engine.Relation, len(op.meta.Partitions)) - } - op.raw.OperatorBase = op.OperatorBase - if err = op.raw.Prepare(proc); err != nil { - return err + op.rawContexts = op.raw.MultiUpdateCtx + op.targets = buildPartitionUpdateTargets(op.rawContexts) + for _, target := range op.targets { + if !features.IsPartitioned(target.contexts[0].TableDef.FeatureFlag) { + continue + } + + var err error + target.meta, _, err = proc.GetPartitionService().GetStorage().GetMetadata( + proc.Ctx, + target.tableID, + proc.GetTxnOperator(), + ) + if err != nil { + return err + } + _, _, r, err := op.raw.Engine.GetRelationById( + proc.Ctx, + proc.GetTxnOperator(), + target.tableID, + ) + if err != nil { + return err + } + if len(r.GetExtraInfo().IndexTables) > 0 { + target.mainIndexes = r.GetExtraInfo().IndexTables + target.partitionIndexes = make(map[uint64][]engine.Relation, len(target.meta.Partitions)) + } } op.affectedRows = 0 op.raw.getS3WriterFunc = op.getS3Writer op.raw.getFlushableS3WriterFunc = op.getFlushableS3Writer op.raw.addAffectedRowsFunc = op.doAddAffectedRows - op.writers = make(map[uint64]*s3WriterDelegate, len(op.meta.Partitions)) + op.writers = make(map[uint64]*s3WriterDelegate) + op.nextWriterID = 0 + return nil +} - op.rawTableIDs = make([]uint64, 0, len(op.raw.MultiUpdateCtx)) - op.rawTableFlags = make([]uint64, 0, len(op.raw.MultiUpdateCtx)) - for _, c := range op.raw.MultiUpdateCtx { - op.rawTableIDs = append(op.rawTableIDs, c.TableDef.TblId) - op.rawTableFlags = append(op.rawTableFlags, c.TableDef.FeatureFlag) +func buildPartitionUpdateTargets(contexts []*MultiUpdateCtx) []*partitionUpdateTarget { + targetsByMain := make(map[int]*partitionUpdateTarget) + targets := make([]*partitionUpdateTarget, 0, len(contexts)) + for i, ctx := range contexts { + if features.IsIndexTable(ctx.TableDef.FeatureFlag) { + continue + } + target := &partitionUpdateTarget{ + contexts: []*MultiUpdateCtx{cloneTargetContext(ctx)}, + tableID: ctx.TableDef.TblId, + writerIDs: make(map[uint64]uint64), + } + targetsByMain[i] = target + targets = append(targets, target) } - return nil + for _, ctx := range contexts { + if !features.IsIndexTable(ctx.TableDef.FeatureFlag) { + continue + } + if target := targetsByMain[ctx.TargetUpdateCtxIdx]; target != nil { + target.contexts = append(target.contexts, cloneTargetContext(ctx)) + } + } + return targets +} + +func cloneTargetContext(ctx *MultiUpdateCtx) *MultiUpdateCtx { + cloned := ctx.clone() + cloned.TargetUpdateCtxIdx = 0 + return cloned } func (op *PartitionMultiUpdate) Call( @@ -155,70 +193,10 @@ func (op *PartitionMultiUpdate) writeTable( op.raw.delegated = true op.raw.input = input - pos := int32(-1) - if len(op.raw.MultiUpdateCtx[0].PartitionCols) > 0 { - pos = int32(op.raw.MultiUpdateCtx[0].PartitionCols[0]) - } - - res, err := partitionprune.Prune(proc, input.Batch, op.meta, pos) - if err != nil { - return vm.CallResult{}, err - } - defer res.Close() - if res.Empty() { - panic("Prune result is empty") - } - - var rel engine.Relation - res.Iter( - func( - partition partition.Partition, - bat *batch.Batch, - ) bool { - _, _, rel, err = op.raw.Engine.GetRelationById( - proc.Ctx, - proc.GetTxnOperator(), - partition.PartitionID, - ) - if err != nil { - return false - } - - // mapping all main table and index table to partition's. - // Clone each context to avoid mutating shared plan objects - // that may be read concurrently by other operators. - cloned := make([]*MultiUpdateCtx, len(op.raw.MultiUpdateCtx)) - for i, c := range op.raw.MultiUpdateCtx { - r := rel - if features.IsIndexTable(op.rawTableFlags[i]) { - r, err = op.getPartitionIndex( - proc, - op.rawTableIDs[i], - partition.PartitionID, - rel, - ) - if err != nil { - return false - } - } - - cloned[i] = c.clone() - cloned[i].ObjRef.ObjName = r.GetTableName() - cloned[i].TableDef = r.GetTableDef(proc.Ctx) - } - op.raw.MultiUpdateCtx = cloned - op.raw.resetMultiUpdateCtxs() - if err = op.raw.resetMultiSources(proc); err != nil { - return false - } - op.raw.input = vm.CallResult{Batch: bat} - - _, err = op.raw.Call(proc) - return err == nil - }, - ) - if err != nil { - return vm.CallResult{}, err + for _, target := range op.targets { + if err := op.writeTarget(proc, target, input.Batch); err != nil { + return vm.CallResult{}, err + } } return input, nil } @@ -250,74 +228,118 @@ func (op *PartitionMultiUpdate) writeS3( continue } - pos := int32(-1) - if len(op.raw.MultiUpdateCtx[0].PartitionCols) > 0 { - pos = int32(op.raw.MultiUpdateCtx[0].PartitionCols[0]) + for _, target := range op.targets { + if err := op.writeTarget(proc, target, input.Batch); err != nil { + return vm.CallResult{}, err + } } - res, err := partitionprune.Prune(proc, input.Batch, op.meta, pos) + } +} + +func (op *PartitionMultiUpdate) writeTarget( + proc *process.Process, + target *partitionUpdateTarget, + input *batch.Batch, +) error { + if !features.IsPartitioned(target.contexts[0].TableDef.FeatureFlag) { + return op.callRawTarget(proc, target, target.contexts, target.tableID, input) + } + + pos := int32(-1) + if len(target.contexts[0].PartitionCols) > 0 { + pos = int32(target.contexts[0].PartitionCols[0]) + } + res, err := partitionprune.Prune(proc, input, target.meta, pos) + if err != nil { + return err + } + defer res.Close() + if res.Empty() { + panic("Prune result is empty") + } + + res.Iter(func(p partition.Partition, bat *batch.Batch) bool { + var rel engine.Relation + _, _, rel, err = op.raw.Engine.GetRelationById( + proc.Ctx, + proc.GetTxnOperator(), + p.PartitionID, + ) if err != nil { - return vm.CallResult{}, err - } - if res.Empty() { - panic("Prune result is empty") + return false } - var rel engine.Relation - res.Iter( - func( - partition partition.Partition, - bat *batch.Batch, - ) bool { - _, _, rel, err = op.raw.Engine.GetRelationById( - proc.Ctx, - proc.GetTxnOperator(), - partition.PartitionID, + contexts := make([]*MultiUpdateCtx, len(target.contexts)) + for i, ctx := range target.contexts { + r := rel + if features.IsIndexTable(ctx.TableDef.FeatureFlag) { + r, err = op.getPartitionIndex( + proc, + target, + ctx.TableDef.TblId, + p.PartitionID, + rel, ) if err != nil { return false } + } + contexts[i] = ctx.clone() + contexts[i].ObjRef.ObjName = r.GetTableName() + contexts[i].TableDef = r.GetTableDef(proc.Ctx) + } + err = op.callRawTarget(proc, target, contexts, p.PartitionID, bat) + return err == nil + }) + return err +} - old := op.raw.MultiUpdateCtx - new := make([]*MultiUpdateCtx, 0, len(old)) - - // mapping all main table and index table to partition's. - for i, c := range op.raw.MultiUpdateCtx { - r := rel - if features.IsIndexTable(op.rawTableFlags[i]) { - r, err = op.getPartitionIndex( - proc, - op.rawTableIDs[i], - partition.PartitionID, - rel, - ) - if err != nil { - return false - } - } - - newC := c.clone() - newC.ObjRef.ObjName = r.GetTableName() - newC.TableDef = r.GetTableDef(proc.Ctx) - new = append(new, newC) - } - op.raw.MultiUpdateCtx = new - - op.raw.resetMultiUpdateCtxs() - if err = op.raw.resetMultiSources(proc); err != nil { - return false - } - op.raw.mainTable = partition.PartitionID - op.raw.input = vm.CallResult{Batch: bat} +func (op *PartitionMultiUpdate) callRawTarget( + proc *process.Process, + target *partitionUpdateTarget, + contexts []*MultiUpdateCtx, + mainTable uint64, + input *batch.Batch, +) error { + op.raw.MultiUpdateCtx = contexts + if op.raw.Action == UpdateWriteTable { + op.raw.cleanTargetBuffers(proc) + } + op.raw.resetMultiUpdateCtxs() + if err := op.raw.resetMultiSources(proc); err != nil { + return err + } + op.raw.mainTable = mainTable + if op.raw.Action == UpdateWriteS3 { + op.raw.mainTable = op.writerID(target, mainTable) + } + op.raw.input = vm.CallResult{Batch: input} + _, err := op.raw.Call(proc) + return err +} - _, err = op.raw.Call(proc) - return err == nil - }, - ) - res.Close() - if err != nil { - return vm.CallResult{}, err +func (update *MultiUpdate) cleanTargetBuffers(proc *process.Process) { + for _, buf := range update.ctr.insertBuf { + if buf != nil { + buf.Clean(proc.Mp()) } } + update.ctr.insertBuf = make([]*batch.Batch, len(update.MultiUpdateCtx)) + for _, buf := range update.ctr.deleteBuf { + if buf != nil { + buf.Clean(proc.Mp()) + } + } + update.ctr.deleteBuf = make([]*batch.Batch, len(update.MultiUpdateCtx)) +} + +func (op *PartitionMultiUpdate) writerID(target *partitionUpdateTarget, physicalTableID uint64) uint64 { + if id, ok := target.writerIDs[physicalTableID]; ok { + return id + } + op.nextWriterID++ + target.writerIDs[physicalTableID] = op.nextWriterID + return op.nextWriterID } func (op *PartitionMultiUpdate) ExecProjection( @@ -334,8 +356,11 @@ func (op *PartitionMultiUpdate) Free( ) { op.raw.Free(proc, pipelineFailed, err) + for _, w := range op.writers { + _ = w.free(proc) + } for _, w := range op.freeWriters { - w.free(proc) + _ = w.free(proc) } } @@ -348,7 +373,16 @@ func (op *PartitionMultiUpdate) Reset( pipelineFailed bool, err error, ) { + op.raw.MultiUpdateCtx = op.rawContexts op.raw.Reset(proc, pipelineFailed, err) + for _, writer := range op.freeWriters { + _ = writer.reset(proc) + } + for id, writer := range op.writers { + _ = writer.reset(proc) + op.freeWriters = append(op.freeWriters, writer) + delete(op.writers, id) + } } func (op *PartitionMultiUpdate) GetOperatorBase() *vm.OperatorBase { @@ -367,18 +401,19 @@ func (op *PartitionMultiUpdate) SetRejectZeroTemporal(reject bool) { func (op *PartitionMultiUpdate) getPartitionIndex( proc *process.Process, + target *partitionUpdateTarget, tableID uint64, partitionID uint64, partitionRel engine.Relation, ) (engine.Relation, error) { - for i, id := range op.mainIndexes { + for i, id := range target.mainIndexes { if id == tableID { - indexes, ok := op.partitionIndexes[partitionID] + indexes, ok := target.partitionIndexes[partitionID] if ok { return indexes[i], nil } - relations := make([]engine.Relation, 0, len(op.meta.Partitions)) + relations := make([]engine.Relation, 0, len(target.meta.Partitions)) for _, index := range partitionRel.GetExtraInfo().IndexTables { _, _, rel, err := op.raw.Engine.GetRelationById( proc.Ctx, @@ -390,7 +425,7 @@ func (op *PartitionMultiUpdate) getPartitionIndex( } relations = append(relations, rel) } - op.partitionIndexes[partitionID] = relations + target.partitionIndexes[partitionID] = relations return relations[i], nil } @@ -445,6 +480,7 @@ func (ctx *MultiUpdateCtx) clone() *MultiUpdateCtx { InsertPkColIdx: ctx.InsertPkColIdx, DedupByTargetRowID: ctx.DedupByTargetRowID, TargetUpdateCtxIdx: ctx.TargetUpdateCtxIdx, + TargetTableID: ctx.TargetTableID, } objRef := *ctx.ObjRef def := *ctx.TableDef diff --git a/pkg/sql/colexec/multi_update/multi_update_partition_test.go b/pkg/sql/colexec/multi_update/multi_update_partition_test.go index 2adb19cd52c73..077d489108e60 100644 --- a/pkg/sql/colexec/multi_update/multi_update_partition_test.go +++ b/pkg/sql/colexec/multi_update/multi_update_partition_test.go @@ -20,6 +20,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/features" "github.com/stretchr/testify/require" ) @@ -48,10 +49,58 @@ func TestPartitionMultiUpdateString(t *testing.T) { require.Equal(t, "MultiUpdate: partition_multi_update", buf.String()) } +func TestBuildPartitionUpdateTargetsKeepsPhysicalTargetsIndependent(t *testing.T) { + contexts := []*MultiUpdateCtx{ + { + ObjRef: &plan.ObjectRef{}, + TableDef: &plan.TableDef{TblId: 10, Name: "plain"}, + TargetUpdateCtxIdx: 0, + }, + { + ObjRef: &plan.ObjectRef{}, + TableDef: &plan.TableDef{ + TblId: 20, + Name: "partitioned", + FeatureFlag: features.Partitioned, + }, + TargetUpdateCtxIdx: 1, + }, + { + ObjRef: &plan.ObjectRef{}, + TableDef: &plan.TableDef{ + TblId: 21, + Name: "partition_index", + FeatureFlag: features.IndexTable, + }, + TargetUpdateCtxIdx: 1, + }, + } + + targets := buildPartitionUpdateTargets(contexts) + + require.Len(t, targets, 2) + require.Equal(t, uint64(10), targets[0].tableID) + require.Len(t, targets[0].contexts, 1) + require.Equal(t, uint64(20), targets[1].tableID) + require.Len(t, targets[1].contexts, 2) + require.Equal(t, 0, targets[1].contexts[1].TargetUpdateCtxIdx) + require.NotSame(t, contexts[1].TableDef, targets[1].contexts[0].TableDef) + require.NotSame(t, contexts[1], targets[1].contexts[0]) +} + +func TestPartitionWriterIDsSeparateAliasesOfSamePhysicalTable(t *testing.T) { + first := &partitionUpdateTarget{writerIDs: make(map[uint64]uint64)} + second := &partitionUpdateTarget{writerIDs: make(map[uint64]uint64)} + op := &PartitionMultiUpdate{} + + firstID := op.writerID(first, 100) + require.Equal(t, firstID, op.writerID(first, 100)) + require.NotEqual(t, firstID, op.writerID(second, 100)) +} + func TestNewPartitionMultiUpdateFrom(t *testing.T) { ps := &PartitionMultiUpdate{ - raw: &MultiUpdate{RejectZeroTemporal: true}, - tableID: 1, + raw: &MultiUpdate{RejectZeroTemporal: true}, } op := NewPartitionMultiUpdateFrom(ps) require.Equal(t, ps.raw.MultiUpdateCtx, op.(*PartitionMultiUpdate).raw.MultiUpdateCtx) @@ -59,7 +108,6 @@ func TestNewPartitionMultiUpdateFrom(t *testing.T) { require.Equal(t, ps.raw.IsOnduplicateKeyUpdate, op.(*PartitionMultiUpdate).raw.IsOnduplicateKeyUpdate) require.Equal(t, ps.raw.Engine, op.(*PartitionMultiUpdate).raw.Engine) require.Equal(t, ps.raw.RejectZeroTemporal, op.(*PartitionMultiUpdate).raw.RejectZeroTemporal) - require.Equal(t, ps.tableID, op.(*PartitionMultiUpdate).tableID) } func TestPartitionMultiUpdateSetRejectZeroTemporalUpdatesWriters(t *testing.T) { diff --git a/pkg/sql/colexec/multi_update/s3writer_delegate.go b/pkg/sql/colexec/multi_update/s3writer_delegate.go index 2d155e5f13de2..813dd07102773 100644 --- a/pkg/sql/colexec/multi_update/s3writer_delegate.go +++ b/pkg/sql/colexec/multi_update/s3writer_delegate.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/hashmap" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/common/rscthrottler" @@ -112,9 +113,11 @@ type s3WriterDelegate struct { batchSize uint64 flushThreshold uint64 - checkSizeCols []int - buf bytes.Buffer - seenTargetRows map[uint64]map[types.Rowid]struct{} + checkSizeCols []int + buf bytes.Buffer + addAffectedRows func(uint64) + seenTargetRows map[uint64]*hashmap.StrHashMap + admitSeenGrowth func(int64) error memController struct { grantedSize int64 @@ -145,7 +148,9 @@ func newS3Writer( insertFreeLists: make([]*containers.BatchFreeList, tableCount), isRemote: update.IsRemote, rejectZeroTemporal: update.RejectZeroTemporal, - seenTargetRows: make(map[uint64]map[types.Rowid]struct{}), + addAffectedRows: update.addAffectedRowsFunc, + seenTargetRows: update.ctr.seenTargetRows, + admitSeenGrowth: update.admitSeenTargetRowsGrowth, } for i := range writer.insertFreeLists { writer.insertFreeLists[i] = containers.NewBatchFreeList(nil, nil, true) @@ -276,6 +281,7 @@ func (writer *s3WriterDelegate) append( } mp := proc.Mp() + seenSizeBefore := writer.seenTargetRowsSize() targetBatches := make(map[int]*batch.Batch) defer func() { for _, targetBatch := range targetBatches { @@ -292,19 +298,25 @@ func (writer *s3WriterDelegate) append( } targetBatch, ok := targetBatches[targetIdx] if !ok { - targetBatch, _, err = filterTargetRows( + var duplicateRows uint64 + targetBatch, _, duplicateRows, err = filterTargetRows( proc, writer.updateCtxs[targetIdx], inBatch, - writer.seenTargetRows, + writer.seenTargetRows[targetTableID(writer.updateCtxs[targetIdx])], ) if err != nil { return err } + writer.addAffectedRows(duplicateRows) targetBatches[targetIdx] = targetBatch } contextBatches[i] = targetBatch } + seenIncrement := writer.seenTargetRowsSize() - seenSizeBefore + if err = writer.admitSeenGrowth(seenIncrement); err != nil { + return err + } // Route insert columns directly to per-table sinkers (no clone). // Auto-spill S3 writes during Write use proc.Ctx; perfcounter tracking @@ -452,6 +464,14 @@ func (writer *s3WriterDelegate) append( return } +func (writer *s3WriterDelegate) seenTargetRowsSize() int64 { + var size int64 + for _, seen := range writer.seenTargetRows { + size += seen.Size() + } + return size +} + func checkMainTableNotNull(proc *process.Process, updateCtx *MultiUpdateCtx, bat *batch.Batch) error { for insertIdx := range updateCtx.InsertCols { col := updateCtx.TableDef.Cols[insertIdx] @@ -881,7 +901,6 @@ func (writer *s3WriterDelegate) reset(proc *process.Process) (err error) { writer.outputBat.CleanOnlyData() } - clear(writer.seenTargetRows) writer.buf.Reset() return } diff --git a/pkg/sql/colexec/multi_update/types.go b/pkg/sql/colexec/multi_update/types.go index 982829b470201..f04e4b11fdc3b 100644 --- a/pkg/sql/colexec/multi_update/types.go +++ b/pkg/sql/colexec/multi_update/types.go @@ -15,9 +15,10 @@ package multi_update import ( + "github.com/matrixorigin/matrixone/pkg/common/hashmap" "github.com/matrixorigin/matrixone/pkg/common/reuse" + "github.com/matrixorigin/matrixone/pkg/common/rscthrottler" "github.com/matrixorigin/matrixone/pkg/container/batch" - "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/sql/plan" "github.com/matrixorigin/matrixone/pkg/vm" "github.com/matrixorigin/matrixone/pkg/vm/engine" @@ -107,7 +108,9 @@ type container struct { insertBuf []*batch.Batch deleteBuf []*batch.Batch - seenTargetRows map[uint64]map[types.Rowid]struct{} + seenTargetRows map[uint64]*hashmap.StrHashMap + seenRowsGrant int64 + seenRowsRSC rscthrottler.RSCThrottler } type MultiUpdateCtx struct { @@ -125,6 +128,9 @@ type MultiUpdateCtx struct { // row_number() partition for every updated target table. DedupByTargetRowID bool TargetUpdateCtxIdx int + // TargetTableID stays logical when a partition wrapper replaces TableDef + // with a physical partition definition. + TargetTableID uint64 } func (update MultiUpdate) TypeName() string { @@ -167,8 +173,7 @@ func (update *MultiUpdate) Reset(proc *process.Process, pipelineFailed bool, err if update.ctr.s3Writer != nil { update.ctr.s3Writer.reset(proc) } - clear(update.ctr.seenTargetRows) - + update.freeSeenTargetRows() update.ctr.state = vm.Build } @@ -192,11 +197,24 @@ func (update *MultiUpdate) Free(proc *process.Process, pipelineFailed bool, err update.ctr.s3Writer.free(proc) update.ctr.s3Writer = nil } + update.freeSeenTargetRows() update.ctr.updateCtxInfos = nil update.ctr.sources = nil } +func (update *MultiUpdate) freeSeenTargetRows() { + if update.ctr.seenRowsGrant > 0 { + update.ctr.seenRowsRSC.Release(update.ctr.seenRowsGrant) + update.ctr.seenRowsGrant = 0 + } + update.ctr.seenRowsRSC = nil + for _, seen := range update.ctr.seenTargetRows { + seen.Free() + } + update.ctr.seenTargetRows = nil +} + func (update *MultiUpdate) ExecProjection(proc *process.Process, input *batch.Batch) (*batch.Batch, error) { return input, nil } diff --git a/pkg/sql/colexec/multi_update/update_test.go b/pkg/sql/colexec/multi_update/update_test.go index 483f287776255..bcbcb0287ab96 100644 --- a/pkg/sql/colexec/multi_update/update_test.go +++ b/pkg/sql/colexec/multi_update/update_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/hashmap" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -116,51 +117,54 @@ func TestFilterTargetRowsKeepsIndependentWholeRows(t *testing.T) { bat.SetRowCount(4) defer bat.Clean(mp) - first, clean, err := filterTargetRows(proc, &MultiUpdateCtx{ + first, clean, duplicateRows, err := filterTargetRows(proc, &MultiUpdateCtx{ TableDef: &plan.TableDef{TblId: 1}, DedupByTargetRowID: true, DeleteCols: []int{0, 4, 1}, }, bat, nil) require.NoError(t, err) require.True(t, clean) + require.Zero(t, duplicateRows) defer first.Clean(mp) require.Equal(t, []int32{10, 30}, vector.MustFixedColWithTypeCheck[int32](first.Vecs[4])) - second, clean, err := filterTargetRows(proc, &MultiUpdateCtx{ + second, clean, duplicateRows, err := filterTargetRows(proc, &MultiUpdateCtx{ TableDef: &plan.TableDef{TblId: 2}, DedupByTargetRowID: true, DeleteCols: []int{2, 4, 3}, }, bat, nil) require.NoError(t, err) require.True(t, clean) + require.Zero(t, duplicateRows) defer second.Clean(mp) require.Equal(t, []int32{10, 20, 40}, vector.MustFixedColWithTypeCheck[int32](second.Vecs[4])) } -func TestFilterTargetRowsDedupsPhysicalRowsAcrossContextsAndBatches(t *testing.T) { +func TestFilterTargetRowsDedupsAliasesAcrossBatchesWithAccountedHashMap(t *testing.T) { proc := testutil.NewProcess(t) mp := proc.Mp() - rowID := types.BuildTestRowid(1, 1) - makeBatch := func(value int32) *batch.Batch { + rowID1 := types.BuildTestRowid(1, 1) + rowID2 := types.BuildTestRowid(1, 2) + makeBatch := func() *batch.Batch { bat := batch.NewWithSize(3) - bat.Vecs[0] = testutil.MakeRowIdVector([]types.Rowid{rowID}, nil, mp) + bat.Vecs[0] = testutil.MakeRowIdVector([]types.Rowid{rowID1, rowID2}, nil, mp) bat.Vecs[1] = testutil.NewInt64Vector( - 1, + 2, types.T_int64.ToType(), mp, false, nil, - []int64{1}, + []int64{1, 1}, ) bat.Vecs[2] = testutil.NewInt32Vector( - 1, + 2, types.T_int32.ToType(), mp, false, nil, - []int32{value}, + []int32{10, 20}, ) - bat.SetRowCount(1) + bat.SetRowCount(2) return bat } updateCtx := &MultiUpdateCtx{ @@ -168,28 +172,70 @@ func TestFilterTargetRowsDedupsPhysicalRowsAcrossContextsAndBatches(t *testing.T DedupByTargetRowID: true, DeleteCols: []int{0, 2, 1}, } - seen := make(map[uint64]map[types.Rowid]struct{}) + seen, err := hashmap.NewStrHashMap(false, mp) + require.NoError(t, err) + defer seen.Free() - firstInput := makeBatch(10) + firstInput := makeBatch() defer firstInput.Clean(mp) - first, clean, err := filterTargetRows(proc, updateCtx, firstInput, seen) + first, clean, duplicateRows, err := filterTargetRows(proc, updateCtx, firstInput, seen) require.NoError(t, err) require.True(t, clean) defer first.Clean(mp) - require.Equal(t, 1, first.RowCount()) + require.Equal(t, 2, first.RowCount()) + require.Zero(t, duplicateRows) - secondInput := makeBatch(20) + secondInput := makeBatch() defer secondInput.Clean(mp) - secondCtx := &MultiUpdateCtx{ - TableDef: &plan.TableDef{TblId: 42}, - DedupByTargetRowID: true, - DeleteCols: []int{0, 2, 1}, - } - second, clean, err := filterTargetRows(proc, secondCtx, secondInput, seen) + second, clean, duplicateRows, err := filterTargetRows(proc, updateCtx, secondInput, seen) require.NoError(t, err) require.True(t, clean) defer second.Clean(mp) - require.Equal(t, 0, second.RowCount()) + require.Zero(t, second.RowCount()) + require.Equal(t, uint64(2), duplicateRows) + require.Positive(t, seen.Size()) +} + +type testSeenRowsThrottler struct { + available int64 + acquired int64 + released int64 +} + +func (m *testSeenRowsThrottler) Refresh() {} +func (m *testSeenRowsThrottler) PrintUsage() {} +func (m *testSeenRowsThrottler) Available() int64 { + return m.available +} +func (m *testSeenRowsThrottler) Acquire(size int64) (int64, bool) { + if size > m.available { + return m.available, false + } + m.available -= size + m.acquired += size + return m.available, true +} +func (m *testSeenRowsThrottler) Release(size int64) int64 { + m.available += size + m.released += size + return m.available +} + +func TestSeenTargetRowsGrowthUsesS3MemoryAdmission(t *testing.T) { + throttler := &testSeenRowsThrottler{available: 128} + update := &MultiUpdate{} + update.ctr.seenRowsRSC = throttler + + require.NoError(t, update.admitSeenTargetRowsGrowth(96)) + require.Equal(t, int64(96), update.ctr.seenRowsGrant) + require.Equal(t, int64(96), throttler.acquired) + require.Error(t, update.admitSeenTargetRowsGrowth(64)) + require.Equal(t, int64(96), update.ctr.seenRowsGrant) + + update.freeSeenTargetRows() + require.Equal(t, int64(96), throttler.released) + require.Zero(t, update.ctr.seenRowsGrant) + require.Nil(t, update.ctr.seenRowsRSC) } func TestRetainedS3InputColsCountsEveryContextCopy(t *testing.T) { diff --git a/pkg/sql/compile/operator.go b/pkg/sql/compile/operator.go index 7c8147e5d7e08..f56de3058688c 100644 --- a/pkg/sql/compile/operator.go +++ b/pkg/sql/compile/operator.go @@ -902,19 +902,30 @@ func constructMultiUpdate( InsertPkColIdx: int(updateCtx.InsertPkColIdx), DedupByTargetRowID: updateCtx.DedupByTargetRowId, TargetUpdateCtxIdx: int(updateCtx.TargetUpdateCtxIdx), + TargetTableID: updateCtx.TableDef.TblId, } } arg.Action = action ps := proc.GetPartitionService() - if !ps.Enabled() || !features.IsPartitioned(node.UpdateCtxList[0].TableDef.FeatureFlag) { + if !ps.Enabled() { return arg, nil } + if !hasPartitionedUpdateTarget(node.UpdateCtxList) { + return arg, nil + } + + return multi_update.NewPartitionMultiUpdate(arg), nil +} - return multi_update.NewPartitionMultiUpdate( - arg, - node.UpdateCtxList[0].TableDef.TblId, - ), nil +func hasPartitionedUpdateTarget(contexts []*plan.UpdateCtx) bool { + for _, updateCtx := range contexts { + if !features.IsIndexTable(updateCtx.TableDef.FeatureFlag) && + features.IsPartitioned(updateCtx.TableDef.FeatureFlag) { + return true + } + } + return false } func constructInsert( diff --git a/pkg/sql/compile/operator_test.go b/pkg/sql/compile/operator_test.go index 8ca4ff68d4ca1..9bd8433822352 100644 --- a/pkg/sql/compile/operator_test.go +++ b/pkg/sql/compile/operator_test.go @@ -41,6 +41,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/sql/colexec/rightdedupjoin" "github.com/matrixorigin/matrixone/pkg/sql/colexec/shuffle" "github.com/matrixorigin/matrixone/pkg/sql/colexec/table_function" + "github.com/matrixorigin/matrixone/pkg/sql/features" plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan" "github.com/matrixorigin/matrixone/pkg/sql/plan/function" "github.com/matrixorigin/matrixone/pkg/testutil" @@ -176,13 +177,26 @@ func TestDupOperatorMergeOrder(t *testing.T) { func TestDupOperatorPartitionMultiUpdate(t *testing.T) { innerOp := multi_update.NewArgument() - op := multi_update.NewPartitionMultiUpdate(innerOp, 1) + op := multi_update.NewPartitionMultiUpdate(innerOp) result := dupOperator(op, 0, 1) if result == nil { t.Fatal("dupOperator returned nil for PartitionMultiUpdate") } } +func TestHasPartitionedUpdateTargetChecksEveryMainContext(t *testing.T) { + contexts := []*plan.UpdateCtx{ + {TableDef: &plan.TableDef{TblId: 1}}, + {TableDef: &plan.TableDef{TblId: 2, FeatureFlag: features.Partitioned}}, + {TableDef: &plan.TableDef{TblId: 3, FeatureFlag: features.IndexTable}}, + } + require.True(t, hasPartitionedUpdateTarget(contexts)) + + contexts[1].TableDef.FeatureFlag = 0 + contexts[2].TableDef.FeatureFlag = features.Partitioned | features.IndexTable + require.False(t, hasPartitionedUpdateTarget(contexts)) +} + func TestDupOperatorMultiUpdateCountDeleteAffectRows(t *testing.T) { op := multi_update.NewArgument() op.Action = multi_update.UpdateWriteTable diff --git a/pkg/sql/compile/remoterun.go b/pkg/sql/compile/remoterun.go index 3df0aa3931957..f647f89fafd9e 100644 --- a/pkg/sql/compile/remoterun.go +++ b/pkg/sql/compile/remoterun.go @@ -1387,6 +1387,7 @@ func convertToVmOperator(opr *pipeline.Instruction, ctx *scopeContext, eng engin InsertPkColIdx: int(muCtx.InsertPkColIdx), DedupByTargetRowID: muCtx.DedupByTargetRowId, TargetUpdateCtxIdx: int(muCtx.TargetUpdateCtxIdx), + TargetTableID: muCtx.TableDef.TblId, } arg.MultiUpdateCtx[i].InsertCols = make([]int, len(muCtx.InsertCols)) diff --git a/pkg/sql/plan/bind_update.go b/pkg/sql/plan/bind_update.go index 01068281c7757..16821b60fddf7 100644 --- a/pkg/sql/plan/bind_update.go +++ b/pkg/sql/plan/bind_update.go @@ -15,7 +15,6 @@ package plan import ( - "context" "fmt" "sort" "strings" @@ -24,7 +23,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/plan" - "github.com/matrixorigin/matrixone/pkg/sql/features" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" planutil "github.com/matrixorigin/matrixone/pkg/sql/util" ) @@ -81,9 +79,6 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) if err != nil { return 0, err } - if err = validateModernMultiTargetUpdate(builder.GetContext(), dmlCtx); err != nil { - return 0, err - } if err = validateUpdateTargetSubqueries(builder.compCtx, stmt, dmlCtx.objRefs, dmlCtx.tableDefs); err != nil { return 0, err } @@ -1361,28 +1356,6 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) return lastNodeID, err } -func validateModernMultiTargetUpdate(ctx context.Context, dmlCtx *DMLContext) error { - updatedTargetCount := 0 - hasPartitionedTarget := false - - for i, updateCols := range dmlCtx.updateCol2Expr { - if len(updateCols) == 0 { - continue - } - updatedTargetCount++ - tableDef := dmlCtx.tableDefs[i] - hasPartitionedTarget = hasPartitionedTarget || features.IsPartitioned(tableDef.FeatureFlag) - } - - if updatedTargetCount > 1 && hasPartitionedTarget { - return newLegacyUpdatePlannerRouteError( - updateRouteReasonMultiTarget, - moerr.NewUnsupportedDML(ctx, "multi-target update with partitioned table"), - ) - } - return nil -} - func (builder *QueryBuilder) mergeSamePhysicalTargetAssignments( bindCtx *BindContext, lastNodeID int32, diff --git a/pkg/sql/plan/build_test.go b/pkg/sql/plan/build_test.go index 32d3a54c56645..c17e9d8e4a60b 100644 --- a/pkg/sql/plan/build_test.go +++ b/pkg/sql/plan/build_test.go @@ -37,6 +37,7 @@ import ( lockpb "github.com/matrixorigin/matrixone/pkg/pb/lock" "github.com/matrixorigin/matrixone/pkg/pb/plan" txnpb "github.com/matrixorigin/matrixone/pkg/pb/txn" + "github.com/matrixorigin/matrixone/pkg/sql/features" "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" @@ -2257,6 +2258,29 @@ func TestMultiTargetUpdateUsesIndependentModernSelectors(t *testing.T) { } } +func TestPartitionedMultiTargetUpdateUsesModernPlan(t *testing.T) { + for _, sql := range []string{ + "UPDATE nation n JOIN nation2 n2 ON n.n_nationkey = n2.n_nationkey " + + "SET n.n_name = n2.n_name, n2.n_comment = n.n_comment", + "UPDATE nation2 n2 JOIN nation n ON n.n_nationkey = n2.n_nationkey " + + "SET n2.n_comment = n.n_comment, n.n_name = n2.n_name", + } { + mock := NewMockOptimizer(true) + mock.ctxt.tables["nation"].FeatureFlag |= features.Partitioned + logicPlan, err := runOneStmt(mock, t, sql) + require.NoError(t, err) + + multiUpdates := 0 + for _, node := range logicPlan.GetQuery().Nodes { + if node.NodeType == plan.Node_MULTI_UPDATE { + multiUpdates++ + require.Len(t, node.UpdateCtxList, 2) + } + } + require.Equal(t, 1, multiUpdates) + } +} + func TestSamePhysicalTargetAliasesShareMergedFinalRows(t *testing.T) { mock := NewMockOptimizer(true) logicPlan, err := runOneStmt( diff --git a/pkg/sql/plan/update_planner_route_test.go b/pkg/sql/plan/update_planner_route_test.go index 487ce743bf558..04a02d9746040 100644 --- a/pkg/sql/plan/update_planner_route_test.go +++ b/pkg/sql/plan/update_planner_route_test.go @@ -24,7 +24,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" planpb "github.com/matrixorigin/matrixone/pkg/pb/plan" - "github.com/matrixorigin/matrixone/pkg/sql/features" "github.com/matrixorigin/matrixone/pkg/sql/parsers" "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" @@ -172,16 +171,6 @@ func TestBindUpdateProducesTypedPlannerRoutes(t *testing.T) { wantRoute updatePlannerRoute wantReason updatePlannerRouteReason }{ - { - name: "multi target with partitioned table", - sql: "UPDATE nation n JOIN nation2 n2 ON n.n_nationkey = n2.n_nationkey " + - "SET n.n_name = 'a', n2.n_comment = 'b'", - prepare: func(mock *MockOptimizer) { - mock.ctxt.tables["nation"].FeatureFlag |= features.Partitioned - }, - wantRoute: updatePlannerLegacy, - wantReason: updateRouteReasonMultiTarget, - }, { name: "foreign key metadata", sql: "UPDATE nation SET n_name = 'x'", diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.result b/test/distributed/cases/dml/update/update_multi_target_modern.result index 035f4ba615354..398c87e1e53ed 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.result +++ b/test/distributed/cases/dml/update/update_multi_target_modern.result @@ -77,6 +77,9 @@ JOIN multi_update_alias_target b ON a.id = b.id SET a.x = 1, b.y = 2; +SELECT ROW_COUNT(); +row_count() +4 SELECT id, x, y FROM multi_update_alias_target ORDER BY id; id x y 1 1 2 diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.sql b/test/distributed/cases/dml/update/update_multi_target_modern.sql index 2fb28e7ea891c..fb77415114e0c 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.sql +++ b/test/distributed/cases/dml/update/update_multi_target_modern.sql @@ -80,6 +80,7 @@ SET a.x = 1, b.y = 2; +SELECT ROW_COUNT(); SELECT id, x, y FROM multi_update_alias_target ORDER BY id; UPDATE multi_update_alias_target a From e28277e2e025d0c4814d7de7224cf5f1b0cf67a3 Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Fri, 31 Jul 2026 15:13:14 +0800 Subject: [PATCH 4/8] ci: refresh PR state after main sync From f84f1c57fcc8abe19ade218b7606ae2f667baad0 Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Fri, 31 Jul 2026 17:08:19 +0800 Subject: [PATCH 5/8] fix(plan): merge same-table update aliases across tuples --- pkg/sql/plan/bind_update.go | 283 ++++++++++++++++-- pkg/sql/plan/bind_update_fk.go | 24 +- pkg/sql/plan/build_test.go | 178 +++-------- pkg/sql/plan/dml_context.go | 34 ++- pkg/sql/plan/update_planner_route_test.go | 39 ++- .../update/update_multi_target_modern.result | 13 + .../dml/update/update_multi_target_modern.sql | 10 + 7 files changed, 409 insertions(+), 172 deletions(-) diff --git a/pkg/sql/plan/bind_update.go b/pkg/sql/plan/bind_update.go index 26cdecb9830e5..0e9d385e4b146 100644 --- a/pkg/sql/plan/bind_update.go +++ b/pkg/sql/plan/bind_update.go @@ -82,6 +82,18 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) if err = validateUpdateTargetSubqueries(builder.compCtx, stmt, dmlCtx.objRefs, dmlCtx.tableDefs); err != nil { return 0, err } + updatedTargetCount := 0 + for i := range dmlCtx.aliases { + if len(dmlCtx.updateCol2Expr[i]) > 0 { + updatedTargetCount++ + } + } + routeUnsupported := func(reason updatePlannerRouteReason, routeErr error) error { + if updatedTargetCount > 1 { + return newUpdatePlannerRouteError(updatePlannerRejected, reason, routeErr) + } + return newLegacyUpdatePlannerRouteError(reason, routeErr) + } onDuplicateAction := plan.Node_FAIL if stmt.Ignore { onDuplicateAction = plan.Node_IGNORE @@ -117,36 +129,37 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) // The MULTI_UPDATE fast path below maintains regular index tables itself. // It cannot rebuild irregular hidden tables when the base-table PK changes - // (for example IVF entries store the origin PK), so ask the caller to use - // the fallback update planner that runs the delete+insert rebuild path. + // (for example IVF entries store the origin PK). A single-target UPDATE + // may use the table-update planner; multi-target UPDATE is rejected + // instead of falling back from the modern planner. if hasIrregularIndex && primaryKeyUpdated(tableDef, dmlCtx.updateCol2Expr[i]) { - return 0, newLegacyUpdatePlannerRouteError( + return 0, routeUnsupported( updateRouteReasonIrregularIndex, moerr.NewUnsupportedDML(builder.GetContext(), "update vector/full-text index"), ) } // Only block if irregular index exists AND indexed columns are being updated. - // bindAndOptimizeUpdateQuery catches this UnsupportedDML and falls back to - // buildTableUpdate, where irregular indexes are rebuilt by the old path. + // A single-target UPDATE may use buildTableUpdate to rebuild irregular + // indexes. Multi-target UPDATE is rejected instead of falling back. if hasIrregularIndex { for colName := range dmlCtx.updateCol2Expr[i] { if irregularIndexCols[colName] { - return 0, newLegacyUpdatePlannerRouteError( + return 0, routeUnsupported( updateRouteReasonIrregularIndex, moerr.NewUnsupportedDML(builder.GetContext(), "update vector/full-text index"), ) } } // An UPDATE that changes a primary key column of an irregular-index table must go - // through the table-update path: for a synchronous FULLTEXT/IVF index that path - // rejects it (its hidden table is keyed by the old PK and would go stale, #25617), - // and for async indexes it maintains them via CDC. This binder cannot maintain - // either, so bail out to it (ErrUnsupportedDML triggers the fallback). + // use the table-update path for a single target: synchronous FULLTEXT/IVF + // rejects it (its hidden table is keyed by the old PK and would go stale, + // #25617), while async indexes maintain it via CDC. Multi-target UPDATE + // is rejected instead of falling back. if tableDef.Pkey != nil { for _, pkColName := range tableDef.Pkey.Names { if _, ok := dmlCtx.updateCol2Expr[i][pkColName]; ok { - return 0, newLegacyUpdatePlannerRouteError( + return 0, routeUnsupported( updateRouteReasonIrregularIndex, moerr.NewUnsupportedDML( builder.GetContext(), @@ -183,7 +196,7 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) for colName, updateExpr := range dmlCtx.updateCol2Expr[i] { if pkAndUkCols[colName] { - return 0, newLegacyUpdatePlannerRouteError( + return 0, routeUnsupported( updateRouteReasonPubSubKey, moerr.NewUnsupportedDML(builder.compCtx.GetContext(), "update pk/uk on pub/sub table"), ) @@ -216,7 +229,7 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) if colDef.Name == colName { if isEnumOrSetPlanType(&colDef.Typ) { if colDef.Typ.AutoIncr { - return 0, newLegacyUpdatePlannerRouteError( + return 0, routeUnsupported( updateRouteReasonAutoIncrement, moerr.NewUnsupportedDML(builder.compCtx.GetContext(), "auto_increment default value"), ) @@ -300,12 +313,6 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) if err != nil { return 0, err } - updatedTargetCount := 0 - for i := range dmlCtx.aliases { - if len(dmlCtx.updateCol2Expr[i]) > 0 { - updatedTargetCount++ - } - } isMultiTargetUpdate := updatedTargetCount > 1 targetRowNumberPos := make([]int32, len(dmlCtx.aliases)) for i := range targetRowNumberPos { @@ -487,7 +494,7 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) for i, tableDef := range dmlCtx.tableDefs { if updateAutoIncrCols[i] && len(affectedUpdateChildFks(tableDef, dmlCtx.aliases[i], newColName2Idx)) > 0 { - return 0, newLegacyUpdatePlannerRouteError( + return 0, routeUnsupported( updateRouteReasonAutoIncrement, moerr.NewUnsupportedDML( builder.compCtx.GetContext(), @@ -1424,6 +1431,20 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignments( if !hasRepeatedPhysicalTarget { return lastNodeID, selectNode, selectNodeTag, nil } + if len(targetsByTableID) == 1 { + for _, targets := range targetsByTableID { + return builder.mergeSamePhysicalTargetAssignmentsAcrossTuples( + bindCtx, + lastNodeID, + selectNode, + selectNodeTag, + dmlCtx, + targets, + oldColName2Idx, + newColName2Idx, + ) + } + } childColExpr := func(pos int32) *plan.Expr { return &plan.Expr{ @@ -1522,6 +1543,228 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignments( return lastNodeID, projectNode, projectTag, nil } +// mergeSamePhysicalTargetAssignmentsAcrossTuples normalizes every writable +// alias of one physical table into a common row image, then groups those images +// by Rowid. This lets assignments made through different aliases meet even +// when those aliases reference the row in different join tuples. +func (builder *QueryBuilder) mergeSamePhysicalTargetAssignmentsAcrossTuples( + bindCtx *BindContext, + lastNodeID int32, + selectNode *plan.Node, + _ int32, + dmlCtx *DMLContext, + targets []int, + oldColName2Idx map[string]int32, + newColName2Idx map[string]int32, +) (int32, *plan.Node, int32, error) { + tableDef := dmlCtx.tableDefs[targets[0]] + updatedColsSet := make(map[string]struct{}) + for _, targetIdx := range targets { + for colName := range dmlCtx.updateCol2Expr[targetIdx] { + updatedColsSet[colName] = struct{}{} + } + } + updatedCols := make([]string, 0, len(updatedColsSet)) + for colName := range updatedColsSet { + updatedCols = append(updatedCols, colName) + } + sort.Strings(updatedCols) + + nullExpr := func(typ plan.Type) *plan.Expr { + expr := makePlan2NullConstExprWithType() + expr.Typ = typ + expr.Typ.NotNullable = false + return expr + } + + // Canonical layout: old physical row followed by one nullable contribution + // for every column assigned through any alias. + canonicalTypes := make([]plan.Type, 0, len(tableDef.Cols)+len(updatedCols)) + for _, col := range tableDef.Cols { + canonicalTypes = append(canonicalTypes, col.Typ) + } + for _, colName := range updatedCols { + colIdx := tableDef.Name2ColIndex[colName] + canonicalTypes = append(canonicalTypes, tableDef.Cols[colIdx].Typ) + } + + branchIDs := make([]int32, 0, len(targets)) + branchTags := make([]int32, 0, len(targets)) + sourceSinkID := appendSinkNode(builder, bindCtx, lastNodeID) + if builder.preserveSinkProjection == nil { + builder.preserveSinkProjection = make(map[int32]struct{}) + } + builder.preserveSinkProjection[sourceSinkID] = struct{}{} + sourceStep := builder.appendStep(sourceSinkID) + for _, sourceIdx := range targets { + sourceAlias := dmlCtx.aliases[sourceIdx] + branchTag := builder.genNewBindTag() + branchScanID := appendSinkScanNode(builder, bindCtx, sourceStep) + if builder.preserveScanProjection == nil { + builder.preserveScanProjection = make(map[int32]struct{}) + } + builder.preserveScanProjection[branchScanID] = struct{}{} + if builder.positionalSinkScans == nil { + builder.positionalSinkScans = make(map[int32]struct{}) + } + builder.positionalSinkScans[branchScanID] = struct{}{} + branchColExpr := func(pos int32) *plan.Expr { + return &plan.Expr{ + Typ: selectNode.ProjectList[pos].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: 0, ColPos: pos}, + }, + } + } + branchProject := make([]*plan.Expr, 0, len(canonicalTypes)) + for _, col := range tableDef.Cols { + branchProject = append( + branchProject, + branchColExpr(oldColName2Idx[sourceAlias+"."+col.Name]), + ) + } + for _, colName := range updatedCols { + if _, updatesCol := dmlCtx.updateCol2Expr[sourceIdx][colName]; updatesCol { + branchProject = append( + branchProject, + branchColExpr(newColName2Idx[sourceAlias+"."+colName]), + ) + } else { + colIdx := tableDef.Name2ColIndex[colName] + branchProject = append(branchProject, nullExpr(tableDef.Cols[colIdx].Typ)) + } + } + branchIDs = append(branchIDs, builder.appendNode(&plan.Node{ + NodeType: plan.Node_PROJECT, + Children: []int32{branchScanID}, + ProjectList: branchProject, + BindingTags: []int32{branchTag}, + }, bindCtx)) + branchTags = append(branchTags, branchTag) + } + + unionID := branchIDs[0] + unionInputTag := branchTags[0] + for branchIdx := 1; branchIdx < len(branchIDs); branchIdx++ { + unionProject := make([]*plan.Expr, len(canonicalTypes)) + for pos, typ := range canonicalTypes { + unionProject[pos] = &plan.Expr{ + Typ: typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: unionInputTag, ColPos: int32(pos)}, + }, + } + } + unionTag := builder.genNewBindTag() + unionID = builder.appendNode(&plan.Node{ + NodeType: plan.Node_UNION_ALL, + Children: []int32{unionID, branchIDs[branchIdx]}, + ProjectList: unionProject, + BindingTags: []int32{unionTag}, + }, bindCtx) + unionInputTag = unionTag + } + unionTag := unionInputTag + + rowIDCanonicalPos := int32(tableDef.Name2ColIndex[catalog.Row_ID]) + groupExpr := &plan.Expr{ + Typ: canonicalTypes[rowIDCanonicalPos], + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: unionTag, ColPos: rowIDCanonicalPos}, + }, + } + groupTag := builder.genNewBindTag() + aggTag := builder.genNewBindTag() + aggList := make([]*plan.Expr, 0, len(canonicalTypes)-1) + canonicalAggPos := make([]int32, len(canonicalTypes)) + for pos, typ := range canonicalTypes { + if int32(pos) == rowIDCanonicalPos { + canonicalAggPos[pos] = -1 + continue + } + input := &plan.Expr{ + Typ: typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: unionTag, ColPos: int32(pos)}, + }, + } + aggExpr, err := BindFuncExprImplByPlanExpr( + builder.GetContext(), + "any_value", + []*plan.Expr{input}, + ) + if err != nil { + return 0, nil, 0, err + } + canonicalAggPos[pos] = int32(len(aggList)) + aggList = append(aggList, aggExpr) + } + aggID := builder.appendNode(&plan.Node{ + NodeType: plan.Node_AGG, + Children: []int32{unionID}, + GroupBy: []*plan.Expr{groupExpr}, + AggList: aggList, + BindingTags: []int32{groupTag, aggTag}, + SpillMem: builder.aggSpillMem, + }, bindCtx) + + canonicalResultExpr := func(pos int32) *plan.Expr { + typ := canonicalTypes[pos] + if pos == rowIDCanonicalPos { + return &plan.Expr{ + Typ: typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: groupTag, ColPos: 0}, + }, + } + } + return &plan.Expr{ + Typ: typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: aggTag, ColPos: canonicalAggPos[pos]}, + }, + } + } + + finalProject := make([]*plan.Expr, len(selectNode.ProjectList)) + for pos, expr := range selectNode.ProjectList { + finalProject[pos] = nullExpr(expr.Typ) + } + updatedCanonicalPos := make(map[string]int32, len(updatedCols)) + for i, colName := range updatedCols { + updatedCanonicalPos[colName] = int32(len(tableDef.Cols) + i) + } + for _, targetIdx := range targets { + alias := dmlCtx.aliases[targetIdx] + for colPos, col := range tableDef.Cols { + finalProject[oldColName2Idx[alias+"."+col.Name]] = + canonicalResultExpr(int32(colPos)) + } + for _, colName := range updatedCols { + key := alias + "." + colName + targetPos, exists := newColName2Idx[key] + if !exists { + oldPos := oldColName2Idx[key] + oldColName2Idx[key] = int32(len(finalProject)) + finalProject = append(finalProject, canonicalResultExpr(int32(tableDef.Name2ColIndex[colName]))) + targetPos = oldPos + newColName2Idx[key] = targetPos + } + finalProject[targetPos] = canonicalResultExpr(updatedCanonicalPos[colName]) + } + } + + projectTag := builder.genNewBindTag() + projectNode := &plan.Node{ + NodeType: plan.Node_PROJECT, + Children: []int32{aggID}, + ProjectList: finalProject, + BindingTags: []int32{projectTag}, + } + projectID := builder.appendNode(projectNode, bindCtx) + return projectID, projectNode, projectTag, nil +} + func collectIrregularIndexUpdateCols(tableDef *plan.TableDef) (bool, map[string]bool) { irregularIndexCols := make(map[string]bool) if tableDef == nil { diff --git a/pkg/sql/plan/bind_update_fk.go b/pkg/sql/plan/bind_update_fk.go index d131c0e718852..c71dae3431e62 100644 --- a/pkg/sql/plan/bind_update_fk.go +++ b/pkg/sql/plan/bind_update_fk.go @@ -35,6 +35,12 @@ func (builder *QueryBuilder) appendUpdateForeignKeyChecks( } var err error + updatedTargets := 0 + for i := range dmlCtx.updateCol2Expr { + if len(dmlCtx.updateCol2Expr[i]) > 0 { + updatedTargets++ + } + } for i, tableDef := range dmlCtx.tableDefs { if len(dmlCtx.updateCol2Expr[i]) == 0 { continue @@ -46,6 +52,7 @@ func (builder *QueryBuilder) appendUpdateForeignKeyChecks( tableDef, alias, newColName2Idx, + updatedTargets > 1, ); err != nil { return 0, 0, nil, err } @@ -207,6 +214,7 @@ func (builder *QueryBuilder) rejectUpdateOfReferencedParentKey( tableDef *plan.TableDef, alias string, newColName2Idx map[string]int32, + multiTarget bool, ) error { if tableDef == nil || len(tableDef.RefChildTbls) == 0 { return nil @@ -247,12 +255,20 @@ func (builder *QueryBuilder) rejectUpdateOfReferencedParentKey( } for _, parentColID := range fk.ForeignCols { if _, ok := newColName2Idx[alias+"."+parentColIDToName[parentColID]]; ok { + routeErr := moerr.NewUnsupportedDML( + builder.GetContext(), + foreignKeyUnsupportedDMLCause, + ) + if multiTarget { + return newUpdatePlannerRouteError( + updatePlannerRejected, + updateRouteReasonForeignKey, + routeErr, + ) + } return newLegacyUpdatePlannerRouteError( updateRouteReasonForeignKey, - moerr.NewUnsupportedDML( - builder.GetContext(), - foreignKeyUnsupportedDMLCause, - ), + routeErr, ) } } diff --git a/pkg/sql/plan/build_test.go b/pkg/sql/plan/build_test.go index f8b7f44eb1f75..d44b6f0ef6b81 100644 --- a/pkg/sql/plan/build_test.go +++ b/pkg/sql/plan/build_test.go @@ -2282,58 +2282,50 @@ func TestPartitionedMultiTargetUpdateUsesModernPlan(t *testing.T) { } func TestSamePhysicalTargetAliasesShareMergedFinalRows(t *testing.T) { - mock := NewMockOptimizer(true) - logicPlan, err := runOneStmt( - mock, - t, - "UPDATE nation a JOIN nation b ON a.n_nationkey = b.n_nationkey "+ - "SET a.n_name = 'a', b.n_comment = 'b'", - ) - require.NoError(t, err) + for _, joinCond := range []string{"=", "<>"} { + mock := NewMockOptimizer(true) + logicPlan, err := runOneStmt( + mock, + t, + "UPDATE nation a JOIN nation b ON a.n_nationkey "+joinCond+" b.n_nationkey "+ + "SET a.n_name = 'a', b.n_comment = 'b'", + ) + require.NoError(t, err) - query := logicPlan.GetQuery() - var multiUpdate *plan.Node - mainContexts := 0 - mergedAssignments := 0 - var walkExpr func(*plan.Expr) - walkExpr = func(expr *plan.Expr) { - if expr == nil { - return - } - if fn := expr.GetF(); fn != nil { - if fn.GetFunc().GetObjName() == "if" { - mergedAssignments++ - } - for _, arg := range fn.Args { - walkExpr(arg) + query := logicPlan.GetQuery() + var multiUpdate *plan.Node + mainContexts := 0 + hasUnionAll := false + hasAggregate := false + for _, node := range query.Nodes { + switch node.NodeType { + case plan.Node_MULTI_UPDATE: + multiUpdate = node + case plan.Node_UNION_ALL: + hasUnionAll = true + case plan.Node_AGG: + hasAggregate = true } } - } - for _, node := range query.Nodes { - if node.NodeType == plan.Node_MULTI_UPDATE { - multiUpdate = node - } - for _, expr := range node.ProjectList { - walkExpr(expr) - } - } - require.NotNil(t, multiUpdate) - var tableID uint64 - for _, updateCtx := range multiUpdate.UpdateCtxList { - if updateCtx.TableDef == nil || updateCtx.TableDef.Name != "nation" { - continue - } - mainContexts++ - require.True(t, updateCtx.DedupByTargetRowId) - if tableID == 0 { - tableID = updateCtx.TableDef.TblId - } else { - require.Equal(t, tableID, updateCtx.TableDef.TblId) + require.NotNil(t, multiUpdate) + require.True(t, hasUnionAll) + require.True(t, hasAggregate) + var tableID uint64 + for _, updateCtx := range multiUpdate.UpdateCtxList { + if updateCtx.TableDef == nil || updateCtx.TableDef.Name != "nation" { + continue + } + mainContexts++ + require.True(t, updateCtx.DedupByTargetRowId) + if tableID == 0 { + tableID = updateCtx.TableDef.TblId + } else { + require.Equal(t, tableID, updateCtx.TableDef.TblId) + } } + require.Equal(t, 2, mainContexts) } - require.Equal(t, 2, mainContexts) - require.GreaterOrEqual(t, mergedAssignments, 2) } func TestUpdatePgStyleFromDedupPicksWholeSourceRow(t *testing.T) { @@ -2710,14 +2702,6 @@ func setMockDefaultExpr(t *testing.T, mock *MockOptimizer, tableName, colName, v } } -func setMockOnUpdateExpr(t *testing.T, mock *MockOptimizer, tableName, colName, value string) { - col := requireMockColumn(t, mock, tableName, colName) - col.OnUpdate = &plan.OnUpdate{ - Expr: makeStringConstExpr(col.Typ, value), - OriginString: value, - } -} - func setMockColumnType(t *testing.T, mock *MockOptimizer, tableName, colName string, typ plan.Type) { col := requireMockColumn(t, mock, tableName, colName) col.Typ = typ @@ -2748,49 +2732,6 @@ func makeStringConstExpr(typ plan.Type, value string) *plan.Expr { } } -func requireFallbackSourceProjectNode(t *testing.T, query *Query, projectLen int, marker string) *Node { - for _, node := range query.Nodes { - if !isFallbackSourceProjectNode(query, node, projectLen, marker) { - continue - } - return node - } - t.Fatalf("missing fallback source project with length %d and marker %q", projectLen, marker) - return nil -} - -func requireFallbackSourceProjectExpr(t *testing.T, query *Query, projectLen int, pos int, marker string) *plan.Expr { - for _, node := range query.Nodes { - if !isFallbackSourceProjectNode(query, node, projectLen, marker) { - continue - } - if pos >= len(node.ProjectList) { - continue - } - return node.ProjectList[pos] - } - t.Fatalf("missing fallback source project with length %d and marker %q", projectLen, marker) - return nil -} - -func isFallbackSourceProjectNode(query *Query, node *Node, projectLen int, marker string) bool { - if node.NodeType != plan.Node_PROJECT || len(node.ProjectList) != projectLen { - return false - } - if len(node.Children) == 1 { - childIdx := node.Children[0] - if childIdx >= 0 && childIdx < int32(len(query.Nodes)) && query.Nodes[childIdx].NodeType == plan.Node_SINK_SCAN { - return false - } - } - for _, expr := range node.ProjectList { - if exprContainsStringLiteral(expr, marker) { - return true - } - } - return false -} - func hasUpdateFromDedupAnyValueAgg(query *Query, groupByLen int) bool { for _, node := range query.Nodes { if node.NodeType != plan.Node_AGG || len(node.GroupBy) != groupByLen { @@ -2909,49 +2850,6 @@ func queryContainsExpr(query *Query, accept func(*plan.Expr) bool) bool { return false } -func nodeContainsStringLiteral(node *Node, value string) bool { - for _, expr := range node.ProjectList { - if exprContainsStringLiteral(expr, value) { - return true - } - } - return false -} - -func assertFallbackUpdateProjectLength(t *testing.T, query *Query, projectLen int) { - for _, node := range query.Nodes { - if node.NodeType != plan.Node_PROJECT || len(node.ProjectList) != projectLen || len(node.Children) != 1 { - continue - } - child := query.Nodes[node.Children[0]] - if child.NodeType != plan.Node_SINK_SCAN { - continue - } - return - } - t.Fatalf("missing fallback update project with length %d", projectLen) -} - -func assertFallbackUpdateAggDedupWithAnyValue(t *testing.T, query *Query) { - foundAgg := false - foundAnyValue := false - for _, node := range query.Nodes { - if node.NodeType != plan.Node_AGG { - continue - } - foundAgg = true - for _, expr := range node.AggList { - if exprContainsFuncName(expr, "any_value") { - foundAnyValue = true - break - } - } - } - if !foundAgg || !foundAnyValue { - t.Fatalf("fallback update should build agg dedup path with any_value, foundAgg=%v foundAnyValue=%v", foundAgg, foundAnyValue) - } -} - func exprContainsFuncName(expr *plan.Expr, name string) bool { switch e := expr.Expr.(type) { case *plan.Expr_F: diff --git a/pkg/sql/plan/dml_context.go b/pkg/sql/plan/dml_context.go index 93ae2fc6eb422..ac0053f253a29 100644 --- a/pkg/sql/plan/dml_context.go +++ b/pkg/sql/plan/dml_context.go @@ -152,10 +152,21 @@ func classifyUpdateTableResolutionError(ctx CompilerContext, stmt *tree.Update, moerr.NewInvalidInput(ctx.GetContext(), "cannot insert/update/delete from external table"), ) case foreignKeyUnsupportedDMLMsg: + if updateHasMultiTableTargetShape(stmt) { + return newUpdatePlannerRouteError( + updatePlannerRejected, + updateRouteReasonForeignKey, + err, + ) + } return newLegacyUpdatePlannerRouteError(updateRouteReasonForeignKey, err) case unsupportedTableTypeDMLMsg: if updateHasMultiTableTargetShape(stmt) { - return newLegacyUpdatePlannerRouteError(updateRouteReasonMultiTarget, err) + return newUpdatePlannerRouteError( + updatePlannerRejected, + updateRouteReasonTableForm, + err, + ) } return newUpdatePlannerRouteError( updatePlannerRejected, @@ -268,13 +279,11 @@ func (dmlCtx *DMLContext) resolveSingleTable( ) error { var tblName, dbName, alias string - if aliasTbl, ok := tbl.(*tree.AliasedTableExpr); ok { - alias = string(aliasTbl.As.Alias) - tbl = aliasTbl.Expr - } - for { - if baseTbl, ok := tbl.(*tree.ParenTableExpr); ok { + if aliasTbl, ok := tbl.(*tree.AliasedTableExpr); ok { + alias = string(aliasTbl.As.Alias) + tbl = aliasTbl.Expr + } else if baseTbl, ok := tbl.(*tree.ParenTableExpr); ok { tbl = baseTbl.Expr } else { break @@ -306,6 +315,17 @@ func (dmlCtx *DMLContext) resolveSingleTable( if baseTbl, ok := tbl.(*tree.TableName); ok { dbName = string(baseTbl.SchemaName) tblName = string(baseTbl.ObjectName) + } else if _, ok := tbl.(*tree.Subquery); ok { + // Derived tables are read-only UPDATE sources. The SELECT binder owns + // their plan; DML target discovery must not route the whole statement to + // the legacy planner merely because a joined source is not writable. + return nil + } else if _, ok := tbl.(*tree.StatementSource); ok { + return nil + } else if _, ok := tbl.(*tree.ParenSelect); ok { + return nil + } else if _, ok := tbl.(*tree.Select); ok { + return nil } else { return moerr.NewUnsupportedDML(ctx.GetContext(), "unsupported table type") } diff --git a/pkg/sql/plan/update_planner_route_test.go b/pkg/sql/plan/update_planner_route_test.go index 5d2773f55ccce..59c0d4010c094 100644 --- a/pkg/sql/plan/update_planner_route_test.go +++ b/pkg/sql/plan/update_planner_route_test.go @@ -111,6 +111,16 @@ func TestClassifyUpdateTableResolutionError(t *testing.T) { wantReason: updateRouteReasonForeignKey, wantText: foreignKeyUnsupportedDMLMsg, }, + { + name: "multi target foreign key never enters legacy", + err: moerr.NewUnsupportedDML( + ctx.GetContext(), + foreignKeyUnsupportedDMLCause, + ), + wantRoute: updatePlannerRejected, + wantReason: updateRouteReasonForeignKey, + wantText: foreignKeyUnsupportedDMLMsg, + }, { name: "iceberg uses specialized planner", err: moerr.NewUnsupportedDML( @@ -155,7 +165,20 @@ func TestClassifyUpdateTableResolutionError(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - classifiedErr := classifyUpdateTableResolutionError(ctx, updateStmt, test.err) + testStmt := updateStmt + if test.name == "multi target foreign key never enters legacy" { + parsed, parseErr := parsers.ParseOne( + ctx.GetContext(), + dialect.MYSQL, + "UPDATE nation a JOIN region b ON a.n_regionkey = b.r_regionkey "+ + "SET a.n_name = 'x', b.r_name = 'y'", + 1, + ) + require.NoError(t, parseErr) + defer parsed.Free() + testStmt = parsed.(*tree.Update) + } + classifiedErr := classifyUpdateTableResolutionError(ctx, testStmt, test.err) route, reason, gotErr := classifyUpdatePlannerError(classifiedErr) require.Equal(t, test.wantRoute, route) require.Equal(t, test.wantReason, reason) @@ -185,6 +208,20 @@ func TestBindUpdateProducesTypedPlannerRoutes(t *testing.T) { wantRoute: updatePlannerLegacy, wantReason: updateRouteReasonIrregularIndex, }, + { + name: "multi target never falls back for irregular index", + sql: "UPDATE nation a JOIN nation b ON a.n_nationkey = b.n_nationkey " + + "SET a.n_comment = 'a', b.n_name = 'b'", + prepare: func(mock *MockOptimizer) { + mock.ctxt.tables["nation"].Indexes = []*planpb.IndexDef{{ + IndexName: "idx", + IndexAlgo: catalog.MoIndexIvfFlatAlgo.ToString(), + Parts: []string{"n_comment"}, + }} + }, + wantRoute: updatePlannerRejected, + wantReason: updateRouteReasonIrregularIndex, + }, { name: "pub sub key", sql: "UPDATE nation SET n_nationkey = 2", diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.result b/test/distributed/cases/dml/update/update_multi_target_modern.result index 398c87e1e53ed..7b29723d4b2ee 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.result +++ b/test/distributed/cases/dml/update/update_multi_target_modern.result @@ -93,6 +93,19 @@ SELECT id, x, y FROM multi_update_alias_target ORDER BY id; id x y 1 3 4 2 1 2 +UPDATE multi_update_alias_target SET x = 0, y = 0; +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id <> b.id +SET +a.x = 1, +b.y = 2; +SELECT ROW_COUNT(); +row_count() +4 +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; +id x y +1 1 2 +2 1 2 DROP TABLE multi_update_alias_target; DROP TABLE IF EXISTS multi_update_partition_target; DROP TABLE IF EXISTS multi_update_plain_target; diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.sql b/test/distributed/cases/dml/update/update_multi_target_modern.sql index fb77415114e0c..27591d9e12ad1 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.sql +++ b/test/distributed/cases/dml/update/update_multi_target_modern.sql @@ -90,6 +90,16 @@ SET b.y = 4; SELECT id, x, y FROM multi_update_alias_target ORDER BY id; + +UPDATE multi_update_alias_target SET x = 0, y = 0; +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id <> b.id +SET + a.x = 1, + b.y = 2; + +SELECT ROW_COUNT(); +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; DROP TABLE multi_update_alias_target; DROP TABLE IF EXISTS multi_update_partition_target; From d527e41d128b96f0bae4affecb58b62b36e91f1b Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Fri, 31 Jul 2026 19:40:52 +0800 Subject: [PATCH 6/8] fix multi-target alias update merging --- pkg/sql/colexec/multi_update/multi_update.go | 17 +- pkg/sql/colexec/multi_update/update_test.go | 53 ++ pkg/sql/plan/bind_update.go | 617 +++++++++++++----- pkg/sql/plan/build_test.go | 27 +- .../update/update_multi_target_modern.result | 49 ++ .../dml/update/update_multi_target_modern.sql | 46 ++ 6 files changed, 639 insertions(+), 170 deletions(-) diff --git a/pkg/sql/colexec/multi_update/multi_update.go b/pkg/sql/colexec/multi_update/multi_update.go index ebddce7cde9fd..438aa03edc8e1 100644 --- a/pkg/sql/colexec/multi_update/multi_update.go +++ b/pkg/sql/colexec/multi_update/multi_update.go @@ -25,6 +25,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/rscthrottler" "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/nulls" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/perfcounter" @@ -469,13 +470,24 @@ func filterTargetRows( rowIDVec := input.Vecs[updateCtx.DeleteCols[0]] rowNumberVec := input.Vecs[updateCtx.DeleteCols[2]] - if rowIDVec.GetType().Oid != types.T_Rowid || rowNumberVec.GetType().Oid != types.T_int64 { + if rowIDVec.GetType().Oid != types.T_Rowid || + rowNumberVec.GetType().Oid != types.T_int64 { return nil, false, 0, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector types") } rowNumbers := vector.MustFixedColWithTypeCheck[int64](rowNumberVec) rowIDNulls := rowIDVec.GetNulls() rowNumberNulls := rowNumberVec.GetNulls() + var active []bool + var activeNulls *nulls.Nulls + if len(updateCtx.DeleteCols) >= 4 { + activeVec := input.Vecs[updateCtx.DeleteCols[3]] + if activeVec.GetType().Oid != types.T_bool { + return nil, false, 0, moerr.NewInternalError(proc.Ctx, "invalid multi-target update selector types") + } + active = vector.MustFixedColWithTypeCheck[bool](activeVec) + activeNulls = activeVec.GetNulls() + } selections := make([]int64, 0, input.RowCount()) for i := 0; i < input.RowCount(); i++ { if rowIDNulls.Contains(uint64(i)) || @@ -483,6 +495,9 @@ func filterTargetRows( rowNumbers[i] != 1 { continue } + if activeNulls != nil && (activeNulls.Contains(uint64(i)) || !active[i]) { + continue + } selections = append(selections, int64(i)) } diff --git a/pkg/sql/colexec/multi_update/update_test.go b/pkg/sql/colexec/multi_update/update_test.go index bcbcb0287ab96..99cda08332eac 100644 --- a/pkg/sql/colexec/multi_update/update_test.go +++ b/pkg/sql/colexec/multi_update/update_test.go @@ -196,6 +196,59 @@ func TestFilterTargetRowsDedupsAliasesAcrossBatchesWithAccountedHashMap(t *testi require.Positive(t, seen.Size()) } +func TestFilterTargetRowsSkipsInactivePhysicalTargetBranch(t *testing.T) { + proc := testutil.NewProcess(t) + mp := proc.Mp() + + bat := batch.NewWithSize(4) + bat.Vecs[0] = testutil.MakeRowIdVector( + []types.Rowid{ + types.BuildTestRowid(1, 1), + types.BuildTestRowid(1, 2), + types.BuildTestRowid(1, 3), + }, + nil, + mp, + ) + bat.Vecs[1] = testutil.NewInt64Vector( + 3, + types.T_int64.ToType(), + mp, + false, + nil, + []int64{1, 1, 2}, + ) + bat.Vecs[2] = testutil.NewBoolVector( + 3, + types.T_bool.ToType(), + mp, + false, + nil, + []bool{false, true, true}, + ) + bat.Vecs[3] = testutil.NewInt32Vector( + 3, + types.T_int32.ToType(), + mp, + false, + nil, + []int32{10, 20, 30}, + ) + bat.SetRowCount(3) + defer bat.Clean(mp) + + filtered, clean, duplicateRows, err := filterTargetRows(proc, &MultiUpdateCtx{ + TableDef: &plan.TableDef{TblId: 42}, + DedupByTargetRowID: true, + DeleteCols: []int{0, 3, 1, 2}, + }, bat, nil) + require.NoError(t, err) + require.True(t, clean) + require.Zero(t, duplicateRows) + defer filtered.Clean(mp) + require.Equal(t, []int32{20}, vector.MustFixedColWithTypeCheck[int32](filtered.Vecs[3])) +} + type testSeenRowsThrottler struct { available int64 acquired int64 diff --git a/pkg/sql/plan/bind_update.go b/pkg/sql/plan/bind_update.go index 0e9d385e4b146..0872f646868e9 100644 --- a/pkg/sql/plan/bind_update.go +++ b/pkg/sql/plan/bind_update.go @@ -301,19 +301,23 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) selectNode := builder.qry.Nodes[lastNodeID] selectNodeTag := selectNode.BindingTags[0] - lastNodeID, selectNode, selectNodeTag, err = builder.mergeSamePhysicalTargetAssignments( - bindCtx, - lastNodeID, - selectNode, - selectNodeTag, - dmlCtx, - oldColName2Idx, - newColName2Idx, - ) - if err != nil { - return 0, err - } isMultiTargetUpdate := updatedTargetCount > 1 + targetBranchActivePos := make([]int32, len(dmlCtx.aliases)) + for i := range targetBranchActivePos { + targetBranchActivePos[i] = -1 + } + if isMultiTargetUpdate { + for i := range dmlCtx.aliases { + if len(dmlCtx.updateCol2Expr[i]) == 0 { + continue + } + targetBranchActivePos[i] = int32(len(selectNode.ProjectList)) + selectNode.ProjectList = append( + selectNode.ProjectList, + makePlan2BoolConstExprWithType(true), + ) + } + } targetRowNumberPos := make([]int32, len(dmlCtx.aliases)) for i := range targetRowNumberPos { targetRowNumberPos[i] = -1 @@ -420,6 +424,20 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) } + lastNodeID, selectNode, selectNodeTag, err = builder.mergeSamePhysicalTargetAssignments( + bindCtx, + lastNodeID, + selectNode, + selectNodeTag, + dmlCtx, + oldColName2Idx, + newColName2Idx, + targetBranchActivePos, + ) + if err != nil { + return 0, err + } + if isMultiTargetUpdate { for i, alias := range dmlCtx.aliases { if len(dmlCtx.updateCol2Expr[i]) == 0 { @@ -440,6 +458,7 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) selectNode, selectNodeTag, rowIDPos, + targetBranchActivePos[i], ) if err != nil { return 0, err @@ -1141,7 +1160,6 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) }, } } - finalColName2Idx[alias+"."+col.Name] = int32(finalColIdx) finalProjList = append(finalProjList, finalExpr) } @@ -1172,6 +1190,15 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) }, }, }) + finalProjList = append(finalProjList, &plan.Expr{ + Typ: selectNode.ProjectList[targetBranchActivePos[i]].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: selectNodeTag, + ColPos: targetBranchActivePos[i], + }, + }, + }) } updateCtx := &plan.UpdateCtx{ @@ -1197,6 +1224,10 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) RelPos: finalProjTag, ColPos: targetRowNumberFinalPos[i], }) + updateCtx.DeleteCols = append(updateCtx.DeleteCols, plan.ColRef{ + RelPos: finalProjTag, + ColPos: int32(len(finalProjList) - 1), + }) } updateCtxList = append(updateCtxList, updateCtx) @@ -1412,12 +1443,17 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignments( dmlCtx *DMLContext, oldColName2Idx map[string]int32, newColName2Idx map[string]int32, + targetBranchActivePos []int32, ) (int32, *plan.Node, int32, error) { targetsByTableID := make(map[uint64][]int) + var tableIDs []uint64 for i, updateCols := range dmlCtx.updateCol2Expr { if len(updateCols) > 0 { - targetsByTableID[dmlCtx.tableDefs[i].TblId] = - append(targetsByTableID[dmlCtx.tableDefs[i].TblId], i) + tableID := dmlCtx.tableDefs[i].TblId + if len(targetsByTableID[tableID]) == 0 { + tableIDs = append(tableIDs, tableID) + } + targetsByTableID[tableID] = append(targetsByTableID[tableID], i) } } @@ -1431,212 +1467,417 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignments( if !hasRepeatedPhysicalTarget { return lastNodeID, selectNode, selectNodeTag, nil } - if len(targetsByTableID) == 1 { - for _, targets := range targetsByTableID { - return builder.mergeSamePhysicalTargetAssignmentsAcrossTuples( + + // Reserve the final-row positions needed by every alias before branching. + // Every physical-target branch must expose exactly the same schema to the + // outer UNION ALL, including columns assigned through a sibling alias. + for _, tableID := range tableIDs { + targets := targetsByTableID[tableID] + if len(targets) < 2 { + continue + } + updatedCols := make(map[string]struct{}) + for _, targetIdx := range targets { + for colName := range dmlCtx.updateCol2Expr[targetIdx] { + updatedCols[colName] = struct{}{} + } + } + for _, targetIdx := range targets { + alias := dmlCtx.aliases[targetIdx] + for colName := range updatedCols { + key := alias + "." + colName + if _, exists := newColName2Idx[key]; exists { + continue + } + oldPos := oldColName2Idx[key] + newColName2Idx[key] = oldPos + oldColName2Idx[key] = int32(len(selectNode.ProjectList)) + selectNode.ProjectList = append( + selectNode.ProjectList, + DeepCopyExpr(selectNode.ProjectList[oldPos]), + ) + } + } + } + + sourceSinkID := appendSinkNode(builder, bindCtx, lastNodeID) + if builder.preserveSinkProjection == nil { + builder.preserveSinkProjection = make(map[int32]struct{}) + } + builder.preserveSinkProjection[sourceSinkID] = struct{}{} + sourceStep := builder.appendStep(sourceSinkID) + + sort.SliceStable(tableIDs, func(i, j int) bool { + return len(targetsByTableID[tableIDs[i]]) < len(targetsByTableID[tableIDs[j]]) + }) + branchIDs := make([]int32, 0, len(tableIDs)) + branchNodes := make([]*plan.Node, 0, len(tableIDs)) + for _, tableID := range tableIDs { + targets := targetsByTableID[tableID] + var branchID int32 + var branchNode *plan.Node + var err error + if len(targets) > 1 { + branchID, branchNode, err = builder.mergeSamePhysicalTargetAssignmentsAcrossTuples( bindCtx, - lastNodeID, + sourceStep, selectNode, - selectNodeTag, dmlCtx, targets, oldColName2Idx, newColName2Idx, + targetBranchActivePos, + ) + } else { + branchID, branchNode, err = builder.projectPhysicalTargetSource( + bindCtx, + sourceStep, + selectNode, + dmlCtx, + targets[0], + oldColName2Idx, + newColName2Idx, + targetBranchActivePos, ) } + if err != nil { + return 0, nil, 0, err + } + branchIDs = append(branchIDs, branchID) + branchNodes = append(branchNodes, branchNode) } - childColExpr := func(pos int32) *plan.Expr { - return &plan.Expr{ + if len(branchIDs) > 1 { + for i := range branchIDs { + branchSinkID := appendSinkNode(builder, bindCtx, branchIDs[i]) + if builder.preserveSinkProjection == nil { + builder.preserveSinkProjection = make(map[int32]struct{}) + } + builder.preserveSinkProjection[branchSinkID] = struct{}{} + branchStep := builder.appendStep(branchSinkID) + branchScanID := builder.appendPositionalSinkScan(bindCtx, branchStep) + branchTag := builder.genNewBindTag() + branchProject := make([]*plan.Expr, len(branchNodes[i].ProjectList)) + for pos, expr := range branchNodes[i].ProjectList { + branchProject[pos] = &plan.Expr{ + Typ: expr.Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: 0, ColPos: int32(pos)}, + }, + } + } + materializedNode := &plan.Node{ + NodeType: plan.Node_PROJECT, + Children: []int32{branchScanID}, + ProjectList: branchProject, + BindingTags: []int32{branchTag}, + } + branchIDs[i] = builder.appendNode(materializedNode, bindCtx) + branchNodes[i] = materializedNode + } + } + + unionID := branchIDs[0] + unionNode := branchNodes[0] + unionInputTag := unionNode.BindingTags[0] + for branchIdx := 1; branchIdx < len(branchIDs); branchIdx++ { + unionProject := make([]*plan.Expr, len(unionNode.ProjectList)) + for pos, expr := range unionNode.ProjectList { + unionProject[pos] = &plan.Expr{ + Typ: expr.Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: unionInputTag, ColPos: int32(pos)}, + }, + } + } + unionTag := builder.genNewBindTag() + unionNode = &plan.Node{ + NodeType: plan.Node_UNION_ALL, + Children: []int32{unionID, branchIDs[branchIdx]}, + ProjectList: unionProject, + BindingTags: []int32{unionTag}, + } + unionID = builder.appendNode(unionNode, bindCtx) + unionInputTag = unionTag + } + return unionID, unionNode, unionInputTag, nil +} + +func (builder *QueryBuilder) appendPositionalSinkScan(bindCtx *BindContext, sourceStep int32) int32 { + scanID := appendSinkScanNode(builder, bindCtx, sourceStep) + if builder.preserveScanProjection == nil { + builder.preserveScanProjection = make(map[int32]struct{}) + } + builder.preserveScanProjection[scanID] = struct{}{} + if builder.positionalSinkScans == nil { + builder.positionalSinkScans = make(map[int32]struct{}) + } + builder.positionalSinkScans[scanID] = struct{}{} + return scanID +} + +func nullUpdateProjectionExpr(typ plan.Type) *plan.Expr { + expr := makePlan2NullConstExprWithType() + expr.Typ = typ + expr.Typ.NotNullable = false + return expr +} + +// projectPhysicalTargetSource isolates one physical target from the shared join +// stream. Other targets are NULL so their independent selectors ignore this +// branch after all physical-target streams are combined with UNION ALL. +func (builder *QueryBuilder) projectPhysicalTargetSource( + bindCtx *BindContext, + sourceStep int32, + selectNode *plan.Node, + dmlCtx *DMLContext, + targetIdx int, + oldColName2Idx map[string]int32, + newColName2Idx map[string]int32, + targetBranchActivePos []int32, +) (int32, *plan.Node, error) { + scanID := builder.appendPositionalSinkScan(bindCtx, sourceStep) + project := make([]*plan.Expr, len(selectNode.ProjectList)) + for pos, expr := range selectNode.ProjectList { + project[pos] = &plan.Expr{ + Typ: expr.Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: 0, ColPos: int32(pos)}, + }, + } + } + copyPos := func(pos int32) { + project[pos] = &plan.Expr{ Typ: selectNode.ProjectList[pos].Typ, Expr: &plan.Expr_Col{ - Col: &plan.ColRef{ - RelPos: selectNodeTag, - ColPos: pos, - }, + Col: &plan.ColRef{RelPos: 0, ColPos: pos}, }, } } - projectList := make([]*plan.Expr, len(selectNode.ProjectList)) - for i := range projectList { - projectList[i] = childColExpr(int32(i)) + alias := dmlCtx.aliases[targetIdx] + for _, col := range dmlCtx.tableDefs[targetIdx].Cols { + copyPos(oldColName2Idx[alias+"."+col.Name]) + if pos, ok := newColName2Idx[alias+"."+col.Name]; ok { + copyPos(pos) + } } - - processedTableIDs := make(map[uint64]struct{}) - for targetIdx := range dmlCtx.aliases { - tableID := dmlCtx.tableDefs[targetIdx].TblId - if _, ok := processedTableIDs[tableID]; ok { + targetRowIDPos := oldColName2Idx[alias+"."+catalog.Row_ID] + targetRowIDExpr := DeepCopyExpr(project[targetRowIDPos]) + for otherIdx, activePos := range targetBranchActivePos { + if activePos < 0 { continue } - processedTableIDs[tableID] = struct{}{} - targets := targetsByTableID[tableID] - if len(targets) < 2 { - continue + operator := "isnull" + if otherIdx == targetIdx { + operator = "isnotnull" } - for _, targetIdx := range targets { - targetAlias := dmlCtx.aliases[targetIdx] - targetRowIDPos := oldColName2Idx[targetAlias+"."+catalog.Row_ID] - - for _, col := range dmlCtx.tableDefs[targetIdx].Cols { - var sourceTargets []int - for _, sourceIdx := range targets { - if _, ok := dmlCtx.updateCol2Expr[sourceIdx][col.Name]; ok { - sourceTargets = append(sourceTargets, sourceIdx) - } - } - if len(sourceTargets) == 0 { - continue - } - - key := targetAlias + "." + col.Name - targetOldPos := oldColName2Idx[key] - currentPos, targetUpdatesCol := newColName2Idx[key] - if !targetUpdatesCol { - currentPos = targetOldPos - oldColName2Idx[key] = int32(len(projectList)) - projectList = append(projectList, childColExpr(currentPos)) - newColName2Idx[key] = currentPos - } - - finalExpr := childColExpr(targetOldPos) - for _, sourceIdx := range sourceTargets { - sourceAlias := dmlCtx.aliases[sourceIdx] - sourceRowIDPos := oldColName2Idx[sourceAlias+"."+catalog.Row_ID] - sameRowExpr, err := BindFuncExprImplByPlanExpr( - builder.GetContext(), - "=", - []*plan.Expr{ - childColExpr(targetRowIDPos), - childColExpr(sourceRowIDPos), - }, - ) - if err != nil { - return 0, nil, 0, err - } - sourceValuePos := newColName2Idx[sourceAlias+"."+col.Name] - finalExpr, err = BindFuncExprImplByPlanExpr( - builder.GetContext(), - "if", - []*plan.Expr{ - sameRowExpr, - childColExpr(sourceValuePos), - finalExpr, - }, - ) - if err != nil { - return 0, nil, 0, err - } - } - projectList[currentPos] = finalExpr - } + activeExpr, err := BindFuncExprImplByPlanExpr( + builder.GetContext(), + operator, + []*plan.Expr{DeepCopyExpr(targetRowIDExpr)}, + ) + if err != nil { + return 0, nil, err } + project[activePos] = activeExpr } - - projectTag := builder.genNewBindTag() - projectNode := &plan.Node{ + tag := builder.genNewBindTag() + node := &plan.Node{ NodeType: plan.Node_PROJECT, - Children: []int32{lastNodeID}, - ProjectList: projectList, - BindingTags: []int32{projectTag}, + Children: []int32{scanID}, + ProjectList: project, + BindingTags: []int32{tag}, } - lastNodeID = builder.appendNode(projectNode, bindCtx) - return lastNodeID, projectNode, projectTag, nil + return builder.appendNode(node, bindCtx), node, nil +} + +type physicalTargetContribution struct { + targetIdx int + colName string + valuePos int32 + markerPos int32 +} + +type physicalTargetFiller struct { + sourcePos int32 + canonicalPos int32 + outputPos []int32 } -// mergeSamePhysicalTargetAssignmentsAcrossTuples normalizes every writable -// alias of one physical table into a common row image, then groups those images -// by Rowid. This lets assignments made through different aliases meet even -// when those aliases reference the row in different join tuples. +// mergeSamePhysicalTargetAssignmentsAcrossTuples first selects one complete +// source tuple per alias and target Rowid. Only then does it combine assignments +// made through different aliases, so multiple SET expressions from one alias +// can never be assembled from different source tuples. func (builder *QueryBuilder) mergeSamePhysicalTargetAssignmentsAcrossTuples( bindCtx *BindContext, - lastNodeID int32, + sourceStep int32, selectNode *plan.Node, - _ int32, dmlCtx *DMLContext, targets []int, oldColName2Idx map[string]int32, newColName2Idx map[string]int32, -) (int32, *plan.Node, int32, error) { + targetBranchActivePos []int32, +) (int32, *plan.Node, error) { tableDef := dmlCtx.tableDefs[targets[0]] updatedColsSet := make(map[string]struct{}) + var contributions []physicalTargetContribution for _, targetIdx := range targets { for colName := range dmlCtx.updateCol2Expr[targetIdx] { updatedColsSet[colName] = struct{}{} + contributions = append(contributions, physicalTargetContribution{ + targetIdx: targetIdx, + colName: colName, + }) } } + sort.SliceStable(contributions, func(i, j int) bool { + if contributions[i].targetIdx != contributions[j].targetIdx { + return contributions[i].targetIdx < contributions[j].targetIdx + } + return contributions[i].colName < contributions[j].colName + }) updatedCols := make([]string, 0, len(updatedColsSet)) for colName := range updatedColsSet { updatedCols = append(updatedCols, colName) } sort.Strings(updatedCols) - nullExpr := func(typ plan.Type) *plan.Expr { - expr := makePlan2NullConstExprWithType() - expr.Typ = typ - expr.Typ.NotNullable = false - return expr - } - - // Canonical layout: old physical row followed by one nullable contribution - // for every column assigned through any alias. - canonicalTypes := make([]plan.Type, 0, len(tableDef.Cols)+len(updatedCols)) + // Canonical layout: old physical row, followed by a value and an explicit + // assignment marker for each alias/column contribution. + canonicalTypes := make([]plan.Type, 0, len(tableDef.Cols)+2*len(contributions)) for _, col := range tableDef.Cols { canonicalTypes = append(canonicalTypes, col.Typ) } - for _, colName := range updatedCols { - colIdx := tableDef.Name2ColIndex[colName] + for i := range contributions { + colIdx := tableDef.Name2ColIndex[contributions[i].colName] + contributions[i].valuePos = int32(len(canonicalTypes)) canonicalTypes = append(canonicalTypes, tableDef.Cols[colIdx].Typ) + contributions[i].markerPos = int32(len(canonicalTypes)) + canonicalTypes = append(canonicalTypes, plan.Type{Id: int32(types.T_bool)}) + } + currentTableID := tableDef.TblId + fillerByTableCol := make(map[string]*physicalTargetFiller) + var fillers []*physicalTargetFiller + for targetIdx, updateCols := range dmlCtx.updateCol2Expr { + if len(updateCols) == 0 || dmlCtx.tableDefs[targetIdx].TblId == currentTableID { + continue + } + otherDef := dmlCtx.tableDefs[targetIdx] + otherAlias := dmlCtx.aliases[targetIdx] + for _, col := range otherDef.Cols { + fillerKey := fmt.Sprintf("%d.%s", otherDef.TblId, col.Name) + filler := fillerByTableCol[fillerKey] + if filler == nil { + filler = &physicalTargetFiller{ + sourcePos: oldColName2Idx[otherAlias+"."+col.Name], + canonicalPos: int32(len(canonicalTypes)), + } + fillerByTableCol[fillerKey] = filler + fillers = append(fillers, filler) + canonicalTypes = append(canonicalTypes, col.Typ) + } + filler.outputPos = append(filler.outputPos, oldColName2Idx[otherAlias+"."+col.Name]) + if newPos, ok := newColName2Idx[otherAlias+"."+col.Name]; ok { + filler.outputPos = append(filler.outputPos, newPos) + } + } } - branchIDs := make([]int32, 0, len(targets)) branchTags := make([]int32, 0, len(targets)) - sourceSinkID := appendSinkNode(builder, bindCtx, lastNodeID) - if builder.preserveSinkProjection == nil { - builder.preserveSinkProjection = make(map[int32]struct{}) - } - builder.preserveSinkProjection[sourceSinkID] = struct{}{} - sourceStep := builder.appendStep(sourceSinkID) for _, sourceIdx := range targets { sourceAlias := dmlCtx.aliases[sourceIdx] - branchTag := builder.genNewBindTag() - branchScanID := appendSinkScanNode(builder, bindCtx, sourceStep) - if builder.preserveScanProjection == nil { - builder.preserveScanProjection = make(map[int32]struct{}) + branchScanID := builder.appendPositionalSinkScan(bindCtx, sourceStep) + branchScanTag := builder.genNewBindTag() + builder.qry.Nodes[branchScanID].BindingTags = []int32{branchScanTag} + rowIDPos := oldColName2Idx[sourceAlias+"."+catalog.Row_ID] + dedupInputProject := make([]*plan.Expr, 0, len(selectNode.ProjectList)+1) + for pos, expr := range selectNode.ProjectList { + dedupInputProject = append(dedupInputProject, &plan.Expr{ + Typ: expr.Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: branchScanTag, ColPos: int32(pos)}, + }, + }) } - builder.preserveScanProjection[branchScanID] = struct{}{} - if builder.positionalSinkScans == nil { - builder.positionalSinkScans = make(map[int32]struct{}) + rowIDColExpr := &plan.Expr{ + Typ: selectNode.ProjectList[rowIDPos].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: branchScanTag, ColPos: rowIDPos}, + }, + } + rowIDPartitionExpr, err := BindFuncExprImplByPlanExpr( + builder.GetContext(), + "greatest", + []*plan.Expr{rowIDColExpr, DeepCopyExpr(rowIDColExpr)}, + ) + if err != nil { + return 0, nil, err } - builder.positionalSinkScans[branchScanID] = struct{}{} + dedupInputProject = append(dedupInputProject, rowIDPartitionExpr) + dedupInputTag := builder.genNewBindTag() + dedupInputNode := &plan.Node{ + NodeType: plan.Node_PROJECT, + Children: []int32{branchScanID}, + ProjectList: dedupInputProject, + BindingTags: []int32{dedupInputTag}, + } + dedupInputID := builder.appendNode(dedupInputNode, bindCtx) + dedupID, dedupNode, dedupTag, err := builder.appendRowNumberDedupNode( + bindCtx, + dedupInputID, + dedupInputNode, + dedupInputTag, + []int32{int32(len(selectNode.ProjectList))}, + ) + if err != nil { + return 0, nil, err + } + branchTag := builder.genNewBindTag() branchColExpr := func(pos int32) *plan.Expr { return &plan.Expr{ - Typ: selectNode.ProjectList[pos].Typ, + Typ: dedupNode.ProjectList[pos].Typ, Expr: &plan.Expr_Col{ - Col: &plan.ColRef{RelPos: 0, ColPos: pos}, + Col: &plan.ColRef{RelPos: dedupTag, ColPos: pos}, }, } } branchProject := make([]*plan.Expr, 0, len(canonicalTypes)) for _, col := range tableDef.Cols { + sourcePos := oldColName2Idx[sourceAlias+"."+col.Name] branchProject = append( branchProject, - branchColExpr(oldColName2Idx[sourceAlias+"."+col.Name]), + branchColExpr(sourcePos), ) } - for _, colName := range updatedCols { - if _, updatesCol := dmlCtx.updateCol2Expr[sourceIdx][colName]; updatesCol { + for _, contribution := range contributions { + if contribution.targetIdx == sourceIdx { branchProject = append( branchProject, - branchColExpr(newColName2Idx[sourceAlias+"."+colName]), + branchColExpr(newColName2Idx[sourceAlias+"."+contribution.colName]), + makePlan2BoolConstExprWithType(true), ) } else { - colIdx := tableDef.Name2ColIndex[colName] - branchProject = append(branchProject, nullExpr(tableDef.Cols[colIdx].Typ)) + colIdx := tableDef.Name2ColIndex[contribution.colName] + branchProject = append( + branchProject, + nullUpdateProjectionExpr(tableDef.Cols[colIdx].Typ), + nullUpdateProjectionExpr(plan.Type{Id: int32(types.T_bool)}), + ) + } + } + for _, filler := range fillers { + if sourceIdx == targets[0] { + branchProject = append(branchProject, branchColExpr(filler.sourcePos)) + } else { + branchProject = append( + branchProject, + nullUpdateProjectionExpr(canonicalTypes[filler.canonicalPos]), + ) } } branchIDs = append(branchIDs, builder.appendNode(&plan.Node{ NodeType: plan.Node_PROJECT, - Children: []int32{branchScanID}, + Children: []int32{dedupID}, ProjectList: branchProject, BindingTags: []int32{branchTag}, }, bindCtx)) @@ -1694,7 +1935,7 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignmentsAcrossTuples( []*plan.Expr{input}, ) if err != nil { - return 0, nil, 0, err + return 0, nil, err } canonicalAggPos[pos] = int32(len(aggList)) aggList = append(aggList, aggExpr) @@ -1728,11 +1969,58 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignmentsAcrossTuples( finalProject := make([]*plan.Expr, len(selectNode.ProjectList)) for pos, expr := range selectNode.ProjectList { - finalProject[pos] = nullExpr(expr.Typ) + finalProject[pos] = nullUpdateProjectionExpr(expr.Typ) } - updatedCanonicalPos := make(map[string]int32, len(updatedCols)) - for i, colName := range updatedCols { - updatedCanonicalPos[colName] = int32(len(tableDef.Cols) + i) + for _, filler := range fillers { + for _, outputPos := range filler.outputPos { + finalProject[outputPos] = canonicalResultExpr(filler.canonicalPos) + } + } + targetSet := make(map[int]struct{}, len(targets)) + for _, targetIdx := range targets { + targetSet[targetIdx] = struct{}{} + } + rowIDExpr := canonicalResultExpr(rowIDCanonicalPos) + for targetIdx, activePos := range targetBranchActivePos { + if activePos < 0 { + continue + } + operator := "isnull" + if _, belongsToPhysicalTarget := targetSet[targetIdx]; belongsToPhysicalTarget { + operator = "isnotnull" + } + activeExpr, err := BindFuncExprImplByPlanExpr( + builder.GetContext(), + operator, + []*plan.Expr{DeepCopyExpr(rowIDExpr)}, + ) + if err != nil { + return 0, nil, err + } + finalProject[activePos] = activeExpr + } + finalUpdatedExprs := make(map[string]*plan.Expr, len(updatedCols)) + for _, colName := range updatedCols { + finalExpr := canonicalResultExpr(int32(tableDef.Name2ColIndex[colName])) + for _, contribution := range contributions { + if contribution.colName != colName { + continue + } + var err error + finalExpr, err = BindFuncExprImplByPlanExpr( + builder.GetContext(), + "if", + []*plan.Expr{ + canonicalResultExpr(contribution.markerPos), + canonicalResultExpr(contribution.valuePos), + finalExpr, + }, + ) + if err != nil { + return 0, nil, err + } + } + finalUpdatedExprs[colName] = finalExpr } for _, targetIdx := range targets { alias := dmlCtx.aliases[targetIdx] @@ -1750,7 +2038,7 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignmentsAcrossTuples( targetPos = oldPos newColName2Idx[key] = targetPos } - finalProject[targetPos] = canonicalResultExpr(updatedCanonicalPos[colName]) + finalProject[targetPos] = DeepCopyExpr(finalUpdatedExprs[colName]) } } @@ -1762,7 +2050,7 @@ func (builder *QueryBuilder) mergeSamePhysicalTargetAssignmentsAcrossTuples( BindingTags: []int32{projectTag}, } projectID := builder.appendNode(projectNode, bindCtx) - return projectID, projectNode, projectTag, nil + return projectID, projectNode, nil } func collectIrregularIndexUpdateCols(tableDef *plan.TableDef) (bool, map[string]bool) { @@ -1814,6 +2102,7 @@ func (builder *QueryBuilder) appendTargetRowNumberNode( selectNode *plan.Node, selectNodeTag int32, targetRowIDPos int32, + targetActivePos int32, ) (int32, *plan.Node, int32, int32, error) { childColExpr := func(pos int32) *plan.Expr { e := selectNode.ProjectList[pos] @@ -1834,10 +2123,17 @@ func (builder *QueryBuilder) appendTargetRowNumberNode( } targetRowIDExpr := childColExpr(targetRowIDPos) - partitionBy := []*plan.OrderBySpec{{ - Expr: targetRowIDExpr, - Flag: plan.OrderBySpec_INTERNAL, - }} + targetActiveExpr := childColExpr(targetActivePos) + partitionBy := []*plan.OrderBySpec{ + { + Expr: DeepCopyExpr(targetActiveExpr), + Flag: plan.OrderBySpec_INTERNAL, + }, + { + Expr: DeepCopyExpr(targetRowIDExpr), + Flag: plan.OrderBySpec_INTERNAL, + }, + } windowTag := builder.genNewBindTag() lastNodeID = builder.appendNode(&plan.Node{ NodeType: plan.Node_PARTITION, @@ -1854,9 +2150,12 @@ func (builder *QueryBuilder) appendTargetRowNumberNode( Typ: rowNumberFunc.Typ, Expr: &plan.Expr_W{ W: &plan.WindowSpec{ - WindowFunc: rowNumberFunc, - Name: "row_number", - PartitionBy: []*plan.Expr{targetRowIDExpr}, + WindowFunc: rowNumberFunc, + Name: "row_number", + PartitionBy: []*plan.Expr{ + DeepCopyExpr(targetActiveExpr), + DeepCopyExpr(targetRowIDExpr), + }, Frame: &plan.FrameClause{ Type: plan.FrameClause_ROWS, Start: &plan.FrameBound{ diff --git a/pkg/sql/plan/build_test.go b/pkg/sql/plan/build_test.go index d44b6f0ef6b81..bdfdf913e0fca 100644 --- a/pkg/sql/plan/build_test.go +++ b/pkg/sql/plan/build_test.go @@ -2209,7 +2209,7 @@ func TestMultiTargetUpdateUsesIndependentModernSelectors(t *testing.T) { for _, specExpr := range node.WinSpecList { if spec := specExpr.GetW(); spec != nil && spec.Name == "row_number" && - len(spec.PartitionBy) == 1 { + len(spec.PartitionBy) == 2 { rowNumberWindows++ } } @@ -2235,8 +2235,9 @@ func TestMultiTargetUpdateUsesIndependentModernSelectors(t *testing.T) { updateCtx := mainCtxs[name] require.NotNil(t, updateCtx) require.True(t, updateCtx.DedupByTargetRowId) - require.Len(t, updateCtx.DeleteCols, 3) + require.Len(t, updateCtx.DeleteCols, 4) require.NotEqual(t, updateCtx.DeleteCols[0].ColPos, updateCtx.DeleteCols[2].ColPos) + require.NotEqual(t, updateCtx.DeleteCols[2].ColPos, updateCtx.DeleteCols[3].ColPos) } require.NotEqual( t, @@ -2282,15 +2283,18 @@ func TestPartitionedMultiTargetUpdateUsesModernPlan(t *testing.T) { } func TestSamePhysicalTargetAliasesShareMergedFinalRows(t *testing.T) { - for _, joinCond := range []string{"=", "<>"} { + for _, sql := range []string{ + "UPDATE nation a JOIN nation b ON a.n_nationkey = b.n_nationkey " + + "SET a.n_name = 'a', b.n_comment = 'b'", + "UPDATE nation a JOIN nation b ON a.n_nationkey <> b.n_nationkey " + + "SET a.n_name = 'a', b.n_comment = 'b'", + "UPDATE nation a JOIN nation b ON a.n_nationkey <> b.n_nationkey " + + "JOIN nation2 n2 ON n2.n_nationkey = a.n_nationkey " + + "SET a.n_name = 'a', b.n_comment = 'b', n2.n_name = 'n2'", + } { mock := NewMockOptimizer(true) - logicPlan, err := runOneStmt( - mock, - t, - "UPDATE nation a JOIN nation b ON a.n_nationkey "+joinCond+" b.n_nationkey "+ - "SET a.n_name = 'a', b.n_comment = 'b'", - ) - require.NoError(t, err) + logicPlan, err := runOneStmt(mock, t, sql) + require.NoError(t, err, sql) query := logicPlan.GetQuery() var multiUpdate *plan.Node @@ -2325,6 +2329,9 @@ func TestSamePhysicalTargetAliasesShareMergedFinalRows(t *testing.T) { } } require.Equal(t, 2, mainContexts) + if strings.Contains(sql, "nation2") { + require.Len(t, multiUpdate.UpdateCtxList, 3) + } } } diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.result b/test/distributed/cases/dml/update/update_multi_target_modern.result index 7b29723d4b2ee..cbc46e5f7e62b 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.result +++ b/test/distributed/cases/dml/update/update_multi_target_modern.result @@ -106,6 +106,55 @@ SELECT id, x, y FROM multi_update_alias_target ORDER BY id; id x y 1 1 2 2 1 2 +CREATE TABLE multi_update_alias_source ( +target_id INT, +source_x INT, +source_y INT +); +INSERT INTO multi_update_alias_source VALUES +(1, NULL, 1), +(1, 2, 2), +(2, NULL, 1), +(2, 2, 2); +UPDATE multi_update_alias_target SET x = 0, y = 0; +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id = b.id +JOIN multi_update_alias_source s ON s.target_id = a.id +SET +a.x = s.source_x, +a.y = s.source_y, +b.id = b.id; +SELECT COUNT(*) AS mixed_source_rows +FROM multi_update_alias_target +WHERE NOT ((x IS NULL AND y = 1) OR (x = 2 AND y = 2)); +mixed_source_rows +0 +CREATE TABLE multi_update_third_target ( +id INT PRIMARY KEY, +z INT +); +INSERT INTO multi_update_third_target VALUES (1, 0), (2, 0); +UPDATE multi_update_alias_target SET x = 0, y = 0; +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id <> b.id +JOIN multi_update_third_target u ON u.id = a.id +SET +a.x = 1, +b.y = 2, +u.z = 3; +SELECT ROW_COUNT(); +row_count() +6 +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; +id x y +1 1 2 +2 1 2 +SELECT id, z FROM multi_update_third_target ORDER BY id; +id z +1 3 +2 3 +DROP TABLE multi_update_third_target; +DROP TABLE multi_update_alias_source; DROP TABLE multi_update_alias_target; DROP TABLE IF EXISTS multi_update_partition_target; DROP TABLE IF EXISTS multi_update_plain_target; diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.sql b/test/distributed/cases/dml/update/update_multi_target_modern.sql index 27591d9e12ad1..3dca7953f61c2 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.sql +++ b/test/distributed/cases/dml/update/update_multi_target_modern.sql @@ -100,6 +100,52 @@ SET SELECT ROW_COUNT(); SELECT id, x, y FROM multi_update_alias_target ORDER BY id; + +CREATE TABLE multi_update_alias_source ( + target_id INT, + source_x INT, + source_y INT +); +INSERT INTO multi_update_alias_source VALUES + (1, NULL, 1), + (1, 2, 2), + (2, NULL, 1), + (2, 2, 2); + +UPDATE multi_update_alias_target SET x = 0, y = 0; +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id = b.id +JOIN multi_update_alias_source s ON s.target_id = a.id +SET + a.x = s.source_x, + a.y = s.source_y, + b.id = b.id; + +SELECT COUNT(*) AS mixed_source_rows +FROM multi_update_alias_target +WHERE NOT ((x IS NULL AND y = 1) OR (x = 2 AND y = 2)); + +CREATE TABLE multi_update_third_target ( + id INT PRIMARY KEY, + z INT +); +INSERT INTO multi_update_third_target VALUES (1, 0), (2, 0); + +UPDATE multi_update_alias_target SET x = 0, y = 0; +UPDATE multi_update_alias_target a +JOIN multi_update_alias_target b ON a.id <> b.id +JOIN multi_update_third_target u ON u.id = a.id +SET + a.x = 1, + b.y = 2, + u.z = 3; + +SELECT ROW_COUNT(); +SELECT id, x, y FROM multi_update_alias_target ORDER BY id; +SELECT id, z FROM multi_update_third_target ORDER BY id; + +DROP TABLE multi_update_third_target; +DROP TABLE multi_update_alias_source; DROP TABLE multi_update_alias_target; DROP TABLE IF EXISTS multi_update_partition_target; From 54317d2dec6232f9f8299eca03557c467a0fde27 Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Wed, 5 Aug 2026 15:38:19 +0800 Subject: [PATCH 7/8] fix multi update index-only s3 writes --- .../colexec/multi_update/s3writer_delegate.go | 43 +++++++-------- pkg/sql/colexec/multi_update/update_test.go | 55 +++++++++++++++++++ 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/pkg/sql/colexec/multi_update/s3writer_delegate.go b/pkg/sql/colexec/multi_update/s3writer_delegate.go index 7517d64a7273c..fbfd6785ec6cd 100644 --- a/pkg/sql/colexec/multi_update/s3writer_delegate.go +++ b/pkg/sql/colexec/multi_update/s3writer_delegate.go @@ -165,13 +165,7 @@ func newS3Writer( faultInjected := false - mainIdx := -1 - for i, updateCtx := range update.MultiUpdateCtx { - if mainIdx == -1 && - update.ctr.updateCtxInfos[updateCtx.TableDef.Name].tableType == UpdateMainTable { - mainIdx = i - } - + for _, updateCtx := range update.MultiUpdateCtx { if !faultInjected { faultInjected, _ = objectio.LogCNFlushSmallObjsInjected( updateCtx.TableDef.DbName, updateCtx.TableDef.Name, @@ -182,34 +176,39 @@ func newS3Writer( } writer.updateCtxs = update.MultiUpdateCtx - if mainIdx == -1 { - return nil, moerr.NewInternalErrorNoCtx("multi update has no main table context") - } threshold := InsertWriteS3Threshold if faultInjected { threshold = colexec.FaultInjectedS3Threshold } - upCtx := writer.updateCtxs[mainIdx] - if len(upCtx.DeleteCols) > 0 && len(upCtx.InsertCols) > 0 { - //update - writer.action = actionUpdate - writer.flushThreshold = threshold - } else if len(upCtx.InsertCols) > 0 { - //insert - writer.action = actionInsert - writer.flushThreshold = threshold - } else { - //delete - writer.action = actionDelete + writer.action = s3WriterAction(writer.updateCtxs) + if writer.action == actionDelete { writer.flushThreshold = DeleteWriteS3Threshold + } else { + writer.flushThreshold = threshold } writer.checkSizeCols = retainedS3InputCols(writer.updateCtxs, writer.action) return writer, nil } +func s3WriterAction(updateCtxs []*MultiUpdateCtx) actionType { + hasInsert := false + hasDelete := false + for _, updateCtx := range updateCtxs { + hasInsert = hasInsert || len(updateCtx.InsertCols) > 0 + hasDelete = hasDelete || len(updateCtx.DeleteCols) > 0 + } + if hasInsert && hasDelete { + return actionUpdate + } + if hasInsert { + return actionInsert + } + return actionDelete +} + func retainedS3InputCols(updateCtxs []*MultiUpdateCtx, action actionType) []int { var cols []int for _, updateCtx := range updateCtxs { diff --git a/pkg/sql/colexec/multi_update/update_test.go b/pkg/sql/colexec/multi_update/update_test.go index af74c651a8d2f..be874ca5f9bf3 100644 --- a/pkg/sql/colexec/multi_update/update_test.go +++ b/pkg/sql/colexec/multi_update/update_test.go @@ -395,6 +395,61 @@ func TestRetainedS3InputColsCountsEveryContextCopy(t *testing.T) { ) } +func TestS3WriterActionDoesNotRequireMainTableContext(t *testing.T) { + tests := []struct { + name string + updateCtxs []*MultiUpdateCtx + want actionType + }{ + { + name: "index backfill insert", + updateCtxs: []*MultiUpdateCtx{ + {InsertCols: []int{0, 1}}, + {InsertCols: []int{2, 3}}, + }, + want: actionInsert, + }, + { + name: "delete only", + updateCtxs: []*MultiUpdateCtx{{DeleteCols: []int{0, 1}}}, + want: actionDelete, + }, + { + name: "mixed contexts", + updateCtxs: []*MultiUpdateCtx{ + {InsertCols: []int{0, 1}}, + {DeleteCols: []int{2, 3}}, + }, + want: actionUpdate, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.want, s3WriterAction(test.updateCtxs)) + }) + } +} + +func TestNewS3WriterAllowsIndexOnlyContext(t *testing.T) { + _, _, proc := prepareTestCtx(t, false) + objRef, tableDef := getTestSecondaryIndexTable("index_backfill") + updateCtx := &MultiUpdateCtx{ + ObjRef: objRef, + TableDef: tableDef, + InsertCols: []int{0, 1}, + } + update := &MultiUpdate{MultiUpdateCtx: []*MultiUpdateCtx{updateCtx}} + update.resetMultiUpdateCtxs() + + writer, err := newS3Writer(proc.GetService(), update) + require.NoError(t, err) + require.Equal(t, actionInsert, writer.action) + require.Equal(t, InsertWriteS3Threshold, writer.flushThreshold) + require.Equal(t, []int{0, 1}, writer.checkSizeCols) + require.NoError(t, writer.free(proc)) +} + // update table s3 func TestUpdateS3SingleTable(t *testing.T) { hasUniqueKey := false From 985768d1aa72e6143b2db9191788c72579b6c627 Mon Sep 17 00:00:00 2001 From: Cao Kai Date: Wed, 5 Aug 2026 18:31:14 +0800 Subject: [PATCH 8/8] fix multi-target update integration regressions --- .../multi_update/multi_update_partition.go | 159 +++++++++++++++--- .../multi_update_partition_test.go | 37 ++++ pkg/sql/plan/bind_insert.go | 17 +- pkg/sql/plan/bind_update.go | 94 ++++++++--- pkg/sql/plan/bind_update_test.go | 4 +- pkg/sql/plan/build_test.go | 65 ++++++- pkg/sql/plan/types.go | 11 ++ pkg/sql/plan/update_planner_route_test.go | 45 +++-- .../update/update_multi_target_modern.result | 113 ++++++++++++- .../dml/update/update_multi_target_modern.sql | 92 +++++++++- 10 files changed, 558 insertions(+), 79 deletions(-) diff --git a/pkg/sql/colexec/multi_update/multi_update_partition.go b/pkg/sql/colexec/multi_update/multi_update_partition.go index 64b15b530a886..f004a273c6319 100644 --- a/pkg/sql/colexec/multi_update/multi_update_partition.go +++ b/pkg/sql/colexec/multi_update/multi_update_partition.go @@ -17,6 +17,7 @@ package multi_update import ( "bytes" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/partitionprune" "github.com/matrixorigin/matrixone/pkg/pb/partition" @@ -81,6 +82,18 @@ func (op *PartitionMultiUpdate) OpType() vm.OpType { return vm.PartitionMultiUpdate } +func (op *PartitionMultiUpdate) SetAllocationAccount(account *mpool.AllocationAccount) error { + return op.raw.SetAllocationAccount(account) +} + +func (op *PartitionMultiUpdate) ClearAllocationAccount(account *mpool.AllocationAccount) error { + return op.raw.ClearAllocationAccount(account) +} + +func (op *PartitionMultiUpdate) ActivatesAllocationAccountLifecycle() bool { + return op.raw.ActivatesAllocationAccountLifecycle() +} + func (op *PartitionMultiUpdate) Prepare( proc *process.Process, ) error { @@ -244,6 +257,9 @@ func (op *PartitionMultiUpdate) writeTarget( if !features.IsPartitioned(target.contexts[0].TableDef.FeatureFlag) { return op.callRawTarget(proc, target, target.contexts, target.tableID, input) } + if len(target.contexts[0].PartitionCols) > 1 { + return op.writePartitionKeyUpdate(proc, target, input) + } pos := int32(-1) if len(target.contexts[0].PartitionCols) > 0 { @@ -259,36 +275,129 @@ func (op *PartitionMultiUpdate) writeTarget( } res.Iter(func(p partition.Partition, bat *batch.Batch) bool { - var rel engine.Relation - _, _, rel, err = op.raw.Engine.GetRelationById( - proc.Ctx, - proc.GetTxnOperator(), - p.PartitionID, - ) - if err != nil { + contexts, resolveErr := op.resolvePartitionContexts(proc, target, target.contexts, p) + if resolveErr != nil { + err = resolveErr return false } + err = op.callRawTarget(proc, target, contexts, p.PartitionID, bat) + return err == nil + }) + return err +} - contexts := make([]*MultiUpdateCtx, len(target.contexts)) - for i, ctx := range target.contexts { - r := rel - if features.IsIndexTable(ctx.TableDef.FeatureFlag) { - r, err = op.getPartitionIndex( - proc, - target, - ctx.TableDef.TblId, - p.PartitionID, - rel, - ) - if err != nil { - return false - } +func (op *PartitionMultiUpdate) resolvePartitionContexts( + proc *process.Process, + target *partitionUpdateTarget, + contexts []*MultiUpdateCtx, + p partition.Partition, +) ([]*MultiUpdateCtx, error) { + _, _, rel, err := op.raw.Engine.GetRelationById( + proc.Ctx, + proc.GetTxnOperator(), + p.PartitionID, + ) + if err != nil { + return nil, err + } + + resolved := make([]*MultiUpdateCtx, len(contexts)) + for i, ctx := range contexts { + r := rel + if features.IsIndexTable(ctx.TableDef.FeatureFlag) { + r, err = op.getPartitionIndex( + proc, + target, + ctx.TableDef.TblId, + p.PartitionID, + rel, + ) + if err != nil { + return nil, err } - contexts[i] = ctx.clone() - contexts[i].ObjRef.ObjName = r.GetTableName() - contexts[i].TableDef = r.GetTableDef(proc.Ctx) } - err = op.callRawTarget(proc, target, contexts, p.PartitionID, bat) + resolved[i] = ctx.clone() + resolved[i].ObjRef.ObjName = r.GetTableName() + resolved[i].TableDef = r.GetTableDef(proc.Ctx) + } + return resolved, nil +} + +func (op *PartitionMultiUpdate) writePartitionKeyUpdate( + proc *process.Process, + target *partitionUpdateTarget, + input *batch.Batch, +) error { + mainCtx := target.contexts[0] + filtered, owned, duplicateRows, err := filterTargetRows( + proc, + mainCtx, + input, + op.raw.ctr.seenTargetRows[targetTableID(mainCtx)], + ) + if err != nil { + return err + } + if owned { + defer filtered.Clean(proc.Mp()) + } + op.raw.addAffectedRowsFunc(duplicateRows) + if filtered.RowCount() == 0 { + return nil + } + + deleteContexts := clonePartitionPhaseContexts(target.contexts, true) + if err = op.writePartitionPhase( + proc, target, deleteContexts, mainCtx.PartitionCols[0], filtered, + ); err != nil { + return err + } + insertContexts := clonePartitionPhaseContexts(target.contexts, false) + return op.writePartitionPhase( + proc, target, insertContexts, mainCtx.PartitionCols[1], filtered, + ) +} + +func clonePartitionPhaseContexts( + contexts []*MultiUpdateCtx, + deletePhase bool, +) []*MultiUpdateCtx { + cloned := make([]*MultiUpdateCtx, len(contexts)) + for i, ctx := range contexts { + cloned[i] = ctx.clone() + cloned[i].DedupByTargetRowID = false + if deletePhase { + cloned[i].InsertCols = nil + } else { + cloned[i].DeleteCols = nil + } + } + return cloned +} + +func (op *PartitionMultiUpdate) writePartitionPhase( + proc *process.Process, + target *partitionUpdateTarget, + contexts []*MultiUpdateCtx, + partitionCol int, + input *batch.Batch, +) error { + res, err := partitionprune.Prune(proc, input, target.meta, int32(partitionCol)) + if err != nil { + return err + } + defer res.Close() + if res.Empty() { + return nil + } + + res.Iter(func(p partition.Partition, bat *batch.Batch) bool { + partitionContexts, resolveErr := op.resolvePartitionContexts(proc, target, contexts, p) + if resolveErr != nil { + err = resolveErr + return false + } + err = op.callRawTarget(proc, target, partitionContexts, p.PartitionID, bat) return err == nil }) return err diff --git a/pkg/sql/colexec/multi_update/multi_update_partition_test.go b/pkg/sql/colexec/multi_update/multi_update_partition_test.go index 08364f56a167d..c46b355ea7c5e 100644 --- a/pkg/sql/colexec/multi_update/multi_update_partition_test.go +++ b/pkg/sql/colexec/multi_update/multi_update_partition_test.go @@ -19,12 +19,49 @@ import ( "testing" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/features" "github.com/matrixorigin/matrixone/pkg/testutil" "github.com/stretchr/testify/require" ) +func TestPartitionMultiUpdateForwardsAllocationAccountLifecycle(t *testing.T) { + registry, err := mpool.NewAllocationAccountRegistry(1, 16) + require.NoError(t, err) + account, err := registry.Open(1 << 20) + require.NoError(t, err) + + raw := &MultiUpdate{} + op := &PartitionMultiUpdate{raw: raw} + require.False(t, op.ActivatesAllocationAccountLifecycle()) + require.NoError(t, op.SetAllocationAccount(account)) + require.Same(t, account, raw.allocationAccount) + require.NoError(t, op.ClearAllocationAccount(account)) + require.Nil(t, raw.allocationAccount) +} + +func TestClonePartitionPhaseContextsSeparatesDeleteAndInsert(t *testing.T) { + contexts := []*MultiUpdateCtx{{ + ObjRef: &plan.ObjectRef{}, + TableDef: &plan.TableDef{}, + InsertCols: []int{1, 2}, + DeleteCols: []int{3, 4}, + DedupByTargetRowID: true, + }} + deleteContexts := clonePartitionPhaseContexts(contexts, true) + insertContexts := clonePartitionPhaseContexts(contexts, false) + + require.Empty(t, deleteContexts[0].InsertCols) + require.Equal(t, []int{3, 4}, deleteContexts[0].DeleteCols) + require.Empty(t, insertContexts[0].DeleteCols) + require.Equal(t, []int{1, 2}, insertContexts[0].InsertCols) + require.False(t, deleteContexts[0].DedupByTargetRowID) + require.False(t, insertContexts[0].DedupByTargetRowID) + require.Equal(t, []int{1, 2}, contexts[0].InsertCols) + require.Equal(t, []int{3, 4}, contexts[0].DeleteCols) +} + func TestResetMultiUpdateCtxsClassifiesTemporaryIndexTables(t *testing.T) { uniqueName := "__mo_tmp_018f1f767b9d7f35b2d99b8d7774bde8_db_" + catalog.UniqueIndexTableNamePrefix + "0198fa2b-7cc8-7ed1-b7ae-a3d9c29e75fd" diff --git a/pkg/sql/plan/bind_insert.go b/pkg/sql/plan/bind_insert.go index 00da6ecec2804..ec8d23af08090 100644 --- a/pkg/sql/plan/bind_insert.go +++ b/pkg/sql/plan/bind_insert.go @@ -858,10 +858,23 @@ func (builder *QueryBuilder) buildIrregularMasterDeleteByPk(bindCtx *BindContext // path uses (reduceSinkSinkScanNodes + tempOptimizeForDML). It is a no-op when the // table has no irregular indexes. Shared by the modern INSERT and LOAD paths. func (builder *QueryBuilder) finishIrregularIndexMaintenance(query *plan.Query, bindCtx *BindContext) error { - if len(builder.irregularMaintIndexes) == 0 { + if len(builder.irregularMaintIndexes) == 0 && len(builder.irregularUpdateMaints) == 0 { return nil } - if len(builder.irregularMaintIndexes) > 0 { + if len(builder.irregularUpdateMaints) > 0 { + for _, maint := range builder.irregularUpdateMaints { + builder.irregularMaintSourceStep = maint.sourceStep + builder.irregularMaintDeleteStep = maint.deleteStep + builder.irregularMaintDeletePkPos = maint.deletePkPos + builder.irregularMaintDeletePkTyp = maint.deletePkTyp + builder.irregularMaintIndexes = maint.indexes + builder.irregularMaintTableDef = maint.tableDef + builder.irregularMaintObjRef = maint.objRef + if err := builder.buildIrregularIndexMaintenance(bindCtx); err != nil { + return err + } + } + } else if len(builder.irregularMaintIndexes) > 0 { if err := builder.buildIrregularIndexMaintenance(bindCtx); err != nil { return err } diff --git a/pkg/sql/plan/bind_update.go b/pkg/sql/plan/bind_update.go index 15d3a3a5041a6..89d6d3721a244 100644 --- a/pkg/sql/plan/bind_update.go +++ b/pkg/sql/plan/bind_update.go @@ -358,7 +358,7 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) oldPos := oldColName2Idx[alias+"."+col.Name] newColName2Idx[alias+"."+col.Name] = oldPos - oldColName2Idx[alias+"."+col.Name] = int32(len(selectList)) + oldColName2Idx[alias+"."+col.Name] = int32(len(selectNode.ProjectList)) selectNode.ProjectList = append(selectNode.ProjectList, selectNode.ProjectList[oldPos]) selectNode.ProjectList[oldPos] = newDefExpr } @@ -459,10 +459,6 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) } } - fkChecksEnabled, err := IsForeignKeyChecksEnabled(builder.compCtx) - if err != nil { - return 0, err - } mayDependOnForeignKeys, err := builder.updateMayDependOnForeignKeys( bindCtx, dmlCtx, newColName2Idx) if err != nil { @@ -474,21 +470,6 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) // and generic plan caches rebuild after either session-state transition. builder.qry.HasForeignKeyAction = true } - if fkChecksEnabled { - for i, tableDef := range dmlCtx.tableDefs { - if updateAutoIncrCols[i] && - len(affectedUpdateChildFks(tableDef, dmlCtx.aliases[i], newColName2Idx)) > 0 { - return 0, routeUnsupported( - updateRouteReasonAutoIncrementFK, - moerr.NewUnsupportedDML( - builder.compCtx.GetContext(), - "auto_increment foreign key update", - ), - ) - } - } - } - for i, tableDef := range dmlCtx.tableDefs { if updateAutoIncrCols[i] { lastNodeID = builder.appendNode(&plan.Node{ @@ -1205,6 +1186,37 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) }, }, } + if tableDef.Partition != nil && len(tableDef.Partition.PartitionDefs) > 0 { + partitionColName := getPartitionColName(tableDef.Partition.PartitionDefs[0].Def) + if partitionColPos, ok := finalColName2Idx[alias+"."+partitionColName]; ok { + oldPartitionColPos := partitionColPos + if _, updated := newColName2Idx[alias+"."+partitionColName]; updated { + if partitionColName == tableDef.Pkey.PkeyColName { + oldPartitionColPos = oldPkPos + } else { + oldPartitionColPos = int32(len(finalProjList)) + oldSelectPos := oldColName2Idx[alias+"."+partitionColName] + finalProjList = append(finalProjList, &plan.Expr{ + Typ: selectNode.ProjectList[oldSelectPos].Typ, + Expr: &plan.Expr_Col{Col: &plan.ColRef{ + RelPos: selectNodeTag, + ColPos: oldSelectPos, + }}, + }) + } + } + updateCtx.PartitionCols = []plan.ColRef{{ + RelPos: finalProjTag, + ColPos: oldPartitionColPos, + }} + if oldPartitionColPos != partitionColPos { + updateCtx.PartitionCols = append(updateCtx.PartitionCols, plan.ColRef{ + RelPos: finalProjTag, + ColPos: partitionColPos, + }) + } + } + } if isMultiTargetUpdate { updateCtx.DedupByTargetRowId = true targetUpdateCtxIdx[i] = int32(len(updateCtxList)) @@ -1423,6 +1435,18 @@ func (builder *QueryBuilder) bindUpdate(stmt *tree.Update, bindCtx *BindContext) dmlCtx.tableDefs[i], dmlCtx.objRefs[i], ) + builder.irregularUpdateMaints = append( + builder.irregularUpdateMaints, + irregularUpdateMaintenance{ + sourceStep: builder.irregularMaintSourceStep, + deleteStep: builder.irregularMaintDeleteStep, + deletePkPos: builder.irregularMaintDeletePkPos, + deletePkTyp: builder.irregularMaintDeletePkTyp, + indexes: builder.irregularMaintIndexes, + tableDef: builder.irregularMaintTableDef, + objRef: builder.irregularMaintObjRef, + }, + ) } dmlNode := &plan.Node{ @@ -2124,11 +2148,12 @@ func irregularIndexAffectedByUpdate( } // classifyIrregularIndexesForUpdate separates synchronous inline maintenance -// from CDC-only indexes using plugin metadata. The bool return preserves the -// legacy route only for an affected irregular algorithm that has not migrated to -// the plugin contract (currently MASTER); supported plugin indexes never fall -// back. A synchronous-index PK update is rejected here, before lock/mutation -// nodes are built, because its hidden rows are keyed by the old source PK. +// from CDC-only indexes using plugin metadata. MASTER has no plugin, but shares +// the modern synchronous maintenance pipeline with the plugin-backed indexes. +// The bool return preserves the legacy route only for an affected irregular +// algorithm that has not migrated to either mechanism. A synchronous-index PK +// update is rejected here, before lock/mutation nodes are built, because its +// hidden rows are keyed by the old source PK. func classifyIrregularIndexesForUpdate( ctx context.Context, tableDef *plan.TableDef, @@ -2151,8 +2176,23 @@ func classifyIrregularIndexesForUpdate( p, ok := indexplugin.Get(idxDef.IndexAlgo) if !ok { - if affected || pkUpdated { - return nil, true, nil + if !isModernMaintainedIrregularAlgo(idxDef.IndexAlgo) { + if affected || pkUpdated { + return nil, true, nil + } + continue + } + if pkUpdated { + return nil, false, newUpdatePlannerRouteError( + updatePlannerRejected, + updateRouteReasonIrregularIndex, + moerr.NewUnsupportedDML( + ctx, + "update primary key on a table with a synchronous irregular index"), + ) + } + if affected { + affectedSyncGroups[idxDef.IndexName+"\x00"+idxDef.IndexTableName] = true } continue } diff --git a/pkg/sql/plan/bind_update_test.go b/pkg/sql/plan/bind_update_test.go index d43af707584aa..c9e2fbcf5ad87 100644 --- a/pkg/sql/plan/bind_update_test.go +++ b/pkg/sql/plan/bind_update_test.go @@ -154,10 +154,10 @@ func TestClassifyIrregularIndexesForUpdate(t *testing.T) { updateCols: map[string]tree.Expr{"id": nil}, }, { - name: "unmigrated master stays legacy", + name: "synchronous master uses modern maintenance", tableDef: newTableDef(newIndex("master", catalog.MOIndexMasterAlgo.ToString(), "", "body")), updateCols: map[string]tree.Expr{"body": nil}, - wantLegacy: true, + wantInline: 1, }, } diff --git a/pkg/sql/plan/build_test.go b/pkg/sql/plan/build_test.go index 8b2359b489242..9d5b3355a67e6 100644 --- a/pkg/sql/plan/build_test.go +++ b/pkg/sql/plan/build_test.go @@ -2548,15 +2548,36 @@ func TestMultiTargetUpdateUsesIndependentModernSelectors(t *testing.T) { } func TestPartitionedMultiTargetUpdateUsesModernPlan(t *testing.T) { - for _, sql := range []string{ - "UPDATE nation n JOIN nation2 n2 ON n.n_nationkey = n2.n_nationkey " + - "SET n.n_name = n2.n_name, n2.n_comment = n.n_comment", - "UPDATE nation2 n2 JOIN nation n ON n.n_nationkey = n2.n_nationkey " + - "SET n2.n_comment = n.n_comment, n.n_name = n2.n_name", + for _, test := range []struct { + sql string + partitionColumnCount int + }{ + { + sql: "UPDATE nation n JOIN nation2 n2 ON n.n_nationkey = n2.n_nationkey " + + "SET n.n_name = n2.n_name, n2.n_comment = n.n_comment", + partitionColumnCount: 1, + }, + { + sql: "UPDATE nation2 n2 JOIN nation n ON n.n_nationkey = n2.n_nationkey " + + "SET n2.n_comment = n.n_comment, n.n_name = n2.n_name", + partitionColumnCount: 1, + }, + { + sql: "UPDATE nation n JOIN nation2 n2 ON n.n_nationkey = n2.n_nationkey " + + "SET n.n_nationkey = n.n_nationkey + 10, n2.n_comment = n.n_comment", + partitionColumnCount: 2, + }, } { mock := NewMockOptimizer(true) mock.ctxt.tables["nation"].FeatureFlag |= features.Partitioned - logicPlan, err := runOneStmt(mock, t, sql) + mock.ctxt.tables["nation"].Partition = &plan.Partition{ + PartitionDefs: []*plan.PartitionDef{{ + Def: &plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{Args: []*plan.Expr{ + {Expr: &plan.Expr_Col{Col: &plan.ColRef{Name: "n_nationkey"}}}, + }}}}, + }}, + } + logicPlan, err := runOneStmt(mock, t, test.sql) require.NoError(t, err) multiUpdates := 0 @@ -2564,6 +2585,14 @@ func TestPartitionedMultiTargetUpdateUsesModernPlan(t *testing.T) { if node.NodeType == plan.Node_MULTI_UPDATE { multiUpdates++ require.Len(t, node.UpdateCtxList, 2) + for _, updateCtx := range node.UpdateCtxList { + if updateCtx.TableDef.Name == "nation" { + require.Len(t, updateCtx.PartitionCols, test.partitionColumnCount) + require.NotEqual(t, int32(-1), updateCtx.PartitionCols[0].ColPos) + } else { + require.Empty(t, updateCtx.PartitionCols) + } + } } } require.Equal(t, 1, multiUpdates) @@ -2623,6 +2652,30 @@ func TestSamePhysicalTargetAliasesShareMergedFinalRows(t *testing.T) { } } +func TestModernMultiTargetOnUpdateColumnsKeepActiveSelectorsTyped(t *testing.T) { + for _, sql := range []string{ + "UPDATE nation a JOIN nation b ON a.n_nationkey = b.n_nationkey " + + "SET a.n_name = 'a', b.n_comment = 'b'", + "UPDATE emp, dept SET emp.job = 'a', dept.loc = 'b' " + + "WHERE emp.deptno = dept.deptno", + } { + mock := NewMockOptimizer(true) + setMockOnUpdateExpr(t, mock, "nation", "n_regionkey", "1") + setMockOnUpdateExpr(t, mock, "emp", "sal", "1") + setMockOnUpdateExpr(t, mock, "dept", "dname", "'updated'") + + logicPlan, err := runOneStmt(mock, t, sql) + require.NoError(t, err, sql) + multiUpdates := 0 + for _, node := range logicPlan.GetQuery().Nodes { + if node.NodeType == plan.Node_MULTI_UPDATE { + multiUpdates++ + } + } + require.Equal(t, 1, multiUpdates) + } +} + func TestUpdatePgStyleFromDedupPicksWholeSourceRow(t *testing.T) { mock := NewMockOptimizer(true) diff --git a/pkg/sql/plan/types.go b/pkg/sql/plan/types.go index 4c9ab7d605819..58ded2d906caf 100644 --- a/pkg/sql/plan/types.go +++ b/pkg/sql/plan/types.go @@ -309,6 +309,7 @@ type QueryBuilder struct { irregularMaintTableDef *plan.TableDef irregularMaintObjRef *plan.ObjectRef irregularMaintSkipInsert bool + irregularUpdateMaints []irregularUpdateMaintenance // DML RETURNING consumes an attempt-local row image from a dedicated sink. // The mutation plan and the returning projection use independent SINK_SCAN @@ -334,6 +335,16 @@ type QueryBuilder struct { cteRefs []*CTERef } +type irregularUpdateMaintenance struct { + sourceStep int32 + deleteStep int32 + deletePkPos int32 + deletePkTyp plan.Type + indexes []*plan.IndexDef + tableDef *plan.TableDef + objRef *plan.ObjectRef +} + type OptimizerHints struct { pushDownLimitToScan int pushDownTopThroughLeftJoin int diff --git a/pkg/sql/plan/update_planner_route_test.go b/pkg/sql/plan/update_planner_route_test.go index daec0506547ea..a0239461a69cd 100644 --- a/pkg/sql/plan/update_planner_route_test.go +++ b/pkg/sql/plan/update_planner_route_test.go @@ -356,7 +356,7 @@ func TestBindUpdateForeignKeyRoutingByAffectedColumns(t *testing.T) { )) }) - t.Run("auto increment child key uses legacy final row validation", func(t *testing.T) { + t.Run("auto increment child key uses modern final row validation", func(t *testing.T) { mock := NewMockOptimizer(true) prepareEmpDept(mock) emp := mock.ctxt.tables["emp"] @@ -377,10 +377,9 @@ func TestBindUpdateForeignKeyRoutingByAffectedColumns(t *testing.T) { builder := NewQueryBuilder(planpb.Query_UPDATE, mock.CurrentContext(), false, true) _, err = builder.bindUpdate(stmt.(*tree.Update), NewBindContext(builder, nil)) - require.Error(t, err) - route, reason, _ := classifyUpdatePlannerError(err) - require.Equal(t, updatePlannerLegacy, route) - require.Equal(t, updateRouteReasonAutoIncrementFK, reason) + require.NoError(t, err) + require.Equal(t, 1, countUpdateFkPlanNodes(builder.qry, planpb.Node_PRE_INSERT)) + require.Equal(t, 1, countUpdateFkAsserts(builder.qry)) }) t.Run("disabled checks keep auto increment child key on modern route", func(t *testing.T) { @@ -408,6 +407,23 @@ func TestBindUpdateForeignKeyRoutingByAffectedColumns(t *testing.T) { require.Equal(t, 0, countUpdateFkMarkJoins(query)) }) + t.Run("multi target auto increment child key stays modern", func(t *testing.T) { + mock := NewMockOptimizer(true) + prepareEmpDept(mock) + for _, col := range mock.ctxt.tables["emp"].Cols { + if col.Name == "deptno" { + col.Typ.AutoIncr = true + } + } + + logicPlan, err := runOneStmt(mock, t, + "UPDATE emp, dept SET emp.deptno = DEFAULT, dept.loc = 'changed' "+ + "WHERE emp.deptno = dept.deptno") + require.NoError(t, err) + require.Equal(t, 1, countUpdateFkPlanNodes(logicPlan.GetQuery(), planpb.Node_PRE_INSERT)) + require.Equal(t, 1, countUpdateFkPlanNodes(logicPlan.GetQuery(), planpb.Node_MULTI_UPDATE)) + }) + t.Run("affected restricted parent key stays modern with child probe", func(t *testing.T) { mock := NewMockOptimizer(true) prepareEmpDept(mock) @@ -930,10 +946,14 @@ func TestBindUpdateAutoIncrementRunsBeforeForeignKeys(t *testing.T) { return -1 } - t.Run("child generated key uses legacy final row validation", func(t *testing.T) { + t.Run("child generated key uses modern final row validation", func(t *testing.T) { mock := NewMockOptimizer(true) prepareEmpDept(mock) - mock.ctxt.tables["emp"].Cols[7].Typ.AutoIncr = true + for _, col := range mock.ctxt.tables["emp"].Cols { + if col.Name == "deptno" { + col.Typ.AutoIncr = true + } + } stmt, err := parsers.ParseOne( mock.CurrentContext().GetContext(), dialect.MYSQL, @@ -943,12 +963,11 @@ func TestBindUpdateAutoIncrementRunsBeforeForeignKeys(t *testing.T) { require.NoError(t, err) defer stmt.Free() - builder := NewQueryBuilder(planpb.Query_UPDATE, mock.CurrentContext(), false, true) - _, err = builder.bindUpdate(stmt.(*tree.Update), NewBindContext(builder, nil)) - require.ErrorContains(t, err, "auto_increment foreign key update") - route, reason, _ := classifyUpdatePlannerError(err) - require.Equal(t, updatePlannerLegacy, route) - require.Equal(t, updateRouteReasonAutoIncrementFK, reason) + query := bindDirect(t, mock, "UPDATE emp SET deptno = DEFAULT") + preInsertPos := firstNode(query, func(node *planpb.Node) bool { + return node.NodeType == planpb.Node_PRE_INSERT + }) + require.NotEqual(t, -1, preInsertPos) }) t.Run("parent action on generated key uses legacy planner", func(t *testing.T) { diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.result b/test/distributed/cases/dml/update/update_multi_target_modern.result index cbc46e5f7e62b..e225537e69021 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.result +++ b/test/distributed/cases/dml/update/update_multi_target_modern.result @@ -160,7 +160,8 @@ DROP TABLE IF EXISTS multi_update_partition_target; DROP TABLE IF EXISTS multi_update_plain_target; CREATE TABLE multi_update_partition_target ( id INT PRIMARY KEY, -x INT +x INT, +y INT ) PARTITION BY RANGE (id) ( PARTITION p0 VALUES LESS THAN (2), PARTITION p1 VALUES LESS THAN (MAXVALUE) @@ -169,7 +170,7 @@ CREATE TABLE multi_update_plain_target ( id INT PRIMARY KEY, x INT ); -INSERT INTO multi_update_partition_target VALUES (1, 0), (2, 0); +INSERT INTO multi_update_partition_target VALUES (1, 0, 0), (2, 0, 0); INSERT INTO multi_update_plain_target VALUES (1, 0), (2, 0); UPDATE multi_update_partition_target p JOIN multi_update_plain_target n ON p.id = n.id @@ -197,5 +198,113 @@ SELECT id, x FROM multi_update_plain_target ORDER BY id; id x 1 4 2 4 +UPDATE multi_update_partition_target p +JOIN multi_update_plain_target n ON p.id = n.id +SET +p.id = p.id + 10, +n.x = 9 +WHERE p.id = 1; +SELECT ROW_COUNT(); +row_count() +2 +SELECT id, x FROM multi_update_partition_target ORDER BY id; +id x +2 2 +11 2 +SELECT id, x FROM multi_update_plain_target ORDER BY id; +id x +1 9 +2 4 +UPDATE multi_update_partition_target a +JOIN multi_update_partition_target b ON a.id = b.id +SET +a.x = 7, +b.y = 8; +SELECT ROW_COUNT(); +row_count() +4 +SELECT id, x, y FROM multi_update_partition_target ORDER BY id; +id x y +2 7 8 +11 7 8 DROP TABLE multi_update_partition_target; DROP TABLE multi_update_plain_target; +DROP TABLE IF EXISTS multi_update_on_update; +CREATE TABLE multi_update_on_update ( +id INT PRIMARY KEY, +x INT, +y INT, +updated_at TIMESTAMP DEFAULT '2000-01-01 00:00:00' ON UPDATE CURRENT_TIMESTAMP, +KEY idx_updated_at (updated_at) +); +INSERT INTO multi_update_on_update (id, x, y) VALUES (1, 0, 0), (2, 0, 0); +UPDATE multi_update_on_update a +JOIN multi_update_on_update b ON a.id = b.id +SET +a.x = 1, +b.y = 2; +SELECT id, x, y FROM multi_update_on_update ORDER BY id; +id x y +1 1 2 +2 1 2 +SELECT COUNT(*) FROM multi_update_on_update WHERE updated_at = '2000-01-01 00:00:00'; +count(*) +0 +DROP TABLE multi_update_on_update; +DROP TABLE IF EXISTS multi_update_master_target; +DROP TABLE IF EXISTS multi_update_master_plain; +CREATE TABLE multi_update_master_target ( +id VARCHAR(30) PRIMARY KEY, +a VARCHAR(30), +b VARCHAR(30) +); +CREATE INDEX idx_multi_update_master USING MASTER ON multi_update_master_target(a, b); +CREATE TABLE multi_update_master_plain ( +id VARCHAR(30) PRIMARY KEY, +v VARCHAR(30) +); +INSERT INTO multi_update_master_target VALUES ('1', 'old', 'value'); +INSERT INTO multi_update_master_plain VALUES ('1', 'old'); +UPDATE multi_update_master_target m +JOIN multi_update_master_plain p ON m.id = p.id +SET +m.a = 'changed', +p.v = 'z'; +SELECT * FROM multi_update_master_target WHERE a = 'changed' AND b = 'value'; +id a b +1 changed value +SELECT COUNT(*) FROM multi_update_master_target WHERE a = 'old' AND b = 'value'; +count(*) +0 +SELECT * FROM multi_update_master_plain ORDER BY id; +id v +1 z +DROP TABLE multi_update_master_target; +DROP TABLE multi_update_master_plain; +DROP TABLE IF EXISTS multi_update_auto_child; +DROP TABLE IF EXISTS multi_update_auto_plain; +DROP TABLE IF EXISTS multi_update_auto_parent; +CREATE TABLE multi_update_auto_parent (pid INT PRIMARY KEY); +CREATE TABLE multi_update_auto_child ( +id INT PRIMARY KEY, +pid INT AUTO_INCREMENT, +FOREIGN KEY (pid) REFERENCES multi_update_auto_parent(pid) +); +CREATE TABLE multi_update_auto_plain (id INT PRIMARY KEY, v INT); +INSERT INTO multi_update_auto_parent VALUES (1), (2); +INSERT INTO multi_update_auto_child (id, pid) VALUES (1, 1); +INSERT INTO multi_update_auto_plain VALUES (1, 0); +UPDATE multi_update_auto_child c +JOIN multi_update_auto_plain p ON c.id = p.id +SET +c.pid = DEFAULT, +p.v = 9; +SELECT id, pid FROM multi_update_auto_child; +id pid +1 2 +SELECT id, v FROM multi_update_auto_plain; +id v +1 9 +DROP TABLE multi_update_auto_child; +DROP TABLE multi_update_auto_plain; +DROP TABLE multi_update_auto_parent; diff --git a/test/distributed/cases/dml/update/update_multi_target_modern.sql b/test/distributed/cases/dml/update/update_multi_target_modern.sql index 3dca7953f61c2..b8b6819723239 100644 --- a/test/distributed/cases/dml/update/update_multi_target_modern.sql +++ b/test/distributed/cases/dml/update/update_multi_target_modern.sql @@ -152,7 +152,8 @@ DROP TABLE IF EXISTS multi_update_partition_target; DROP TABLE IF EXISTS multi_update_plain_target; CREATE TABLE multi_update_partition_target ( id INT PRIMARY KEY, - x INT + x INT, + y INT ) PARTITION BY RANGE (id) ( PARTITION p0 VALUES LESS THAN (2), PARTITION p1 VALUES LESS THAN (MAXVALUE) @@ -161,7 +162,7 @@ CREATE TABLE multi_update_plain_target ( id INT PRIMARY KEY, x INT ); -INSERT INTO multi_update_partition_target VALUES (1, 0), (2, 0); +INSERT INTO multi_update_partition_target VALUES (1, 0, 0), (2, 0, 0); INSERT INTO multi_update_plain_target VALUES (1, 0), (2, 0); UPDATE multi_update_partition_target p @@ -182,5 +183,92 @@ SET SELECT id, x FROM multi_update_partition_target ORDER BY id; SELECT id, x FROM multi_update_plain_target ORDER BY id; +UPDATE multi_update_partition_target p +JOIN multi_update_plain_target n ON p.id = n.id +SET + p.id = p.id + 10, + n.x = 9 +WHERE p.id = 1; + +SELECT ROW_COUNT(); +SELECT id, x FROM multi_update_partition_target ORDER BY id; +SELECT id, x FROM multi_update_plain_target ORDER BY id; + +UPDATE multi_update_partition_target a +JOIN multi_update_partition_target b ON a.id = b.id +SET + a.x = 7, + b.y = 8; + +SELECT ROW_COUNT(); +SELECT id, x, y FROM multi_update_partition_target ORDER BY id; + DROP TABLE multi_update_partition_target; DROP TABLE multi_update_plain_target; + +DROP TABLE IF EXISTS multi_update_on_update; +CREATE TABLE multi_update_on_update ( + id INT PRIMARY KEY, + x INT, + y INT, + updated_at TIMESTAMP DEFAULT '2000-01-01 00:00:00' ON UPDATE CURRENT_TIMESTAMP, + KEY idx_updated_at (updated_at) +); +INSERT INTO multi_update_on_update (id, x, y) VALUES (1, 0, 0), (2, 0, 0); +UPDATE multi_update_on_update a +JOIN multi_update_on_update b ON a.id = b.id +SET + a.x = 1, + b.y = 2; +SELECT id, x, y FROM multi_update_on_update ORDER BY id; +SELECT COUNT(*) FROM multi_update_on_update WHERE updated_at = '2000-01-01 00:00:00'; +DROP TABLE multi_update_on_update; + +DROP TABLE IF EXISTS multi_update_master_target; +DROP TABLE IF EXISTS multi_update_master_plain; +CREATE TABLE multi_update_master_target ( + id VARCHAR(30) PRIMARY KEY, + a VARCHAR(30), + b VARCHAR(30) +); +CREATE INDEX idx_multi_update_master USING MASTER ON multi_update_master_target(a, b); +CREATE TABLE multi_update_master_plain ( + id VARCHAR(30) PRIMARY KEY, + v VARCHAR(30) +); +INSERT INTO multi_update_master_target VALUES ('1', 'old', 'value'); +INSERT INTO multi_update_master_plain VALUES ('1', 'old'); +UPDATE multi_update_master_target m +JOIN multi_update_master_plain p ON m.id = p.id +SET + m.a = 'changed', + p.v = 'z'; +SELECT * FROM multi_update_master_target WHERE a = 'changed' AND b = 'value'; +SELECT COUNT(*) FROM multi_update_master_target WHERE a = 'old' AND b = 'value'; +SELECT * FROM multi_update_master_plain ORDER BY id; +DROP TABLE multi_update_master_target; +DROP TABLE multi_update_master_plain; + +DROP TABLE IF EXISTS multi_update_auto_child; +DROP TABLE IF EXISTS multi_update_auto_plain; +DROP TABLE IF EXISTS multi_update_auto_parent; +CREATE TABLE multi_update_auto_parent (pid INT PRIMARY KEY); +CREATE TABLE multi_update_auto_child ( + id INT PRIMARY KEY, + pid INT AUTO_INCREMENT, + FOREIGN KEY (pid) REFERENCES multi_update_auto_parent(pid) +); +CREATE TABLE multi_update_auto_plain (id INT PRIMARY KEY, v INT); +INSERT INTO multi_update_auto_parent VALUES (1), (2); +INSERT INTO multi_update_auto_child (id, pid) VALUES (1, 1); +INSERT INTO multi_update_auto_plain VALUES (1, 0); +UPDATE multi_update_auto_child c +JOIN multi_update_auto_plain p ON c.id = p.id +SET + c.pid = DEFAULT, + p.v = 9; +SELECT id, pid FROM multi_update_auto_child; +SELECT id, v FROM multi_update_auto_plain; +DROP TABLE multi_update_auto_child; +DROP TABLE multi_update_auto_plain; +DROP TABLE multi_update_auto_parent;