diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go index 4db49b31d71b8..87a9d46550ce8 100644 --- a/pkg/pb/plan/plan.pb.go +++ b/pkg/pb/plan/plan.pb.go @@ -6643,12 +6643,21 @@ type Node struct { DedupColTypes []Type `protobuf:"bytes,68,rep,name=dedup_col_types,json=dedupColTypes,proto3" json:"dedup_col_types"` RollupFilter bool `protobuf:"varint,69,opt,name=rollup_filter,json=rollupFilter,proto3" json:"rollup_filter,omitempty"` // compile time performance setting - SpillMem int64 `protobuf:"varint,70,opt,name=spill_mem,json=spillMem,proto3" json:"spill_mem,omitempty"` - OriginViews []string `protobuf:"bytes,71,rep,name=origin_views,json=originViews,proto3" json:"origin_views,omitempty"` - DirectView string `protobuf:"bytes,72,opt,name=direct_view,json=directView,proto3" json:"direct_view,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + SpillMem int64 `protobuf:"varint,70,opt,name=spill_mem,json=spillMem,proto3" json:"spill_mem,omitempty"` + OriginViews []string `protobuf:"bytes,71,rep,name=origin_views,json=originViews,proto3" json:"origin_views,omitempty"` + DirectView string `protobuf:"bytes,72,opt,name=direct_view,json=directView,proto3" json:"direct_view,omitempty"` + // TIME_WINDOW only: the GROUP BY columns other than the window's own + // timestamp key. The operator restarts its window state whenever these + // change, so each group gets its own windows instead of one global stream. + TimeWindowPartitionBy []*Expr `protobuf:"bytes,73,rep,name=time_window_partition_by,json=timeWindowPartitionBy,proto3" json:"time_window_partition_by,omitempty"` + // FILL only: positions of the partition keys inside this node's input batch + // (the child TIME_WINDOW's projection). fill(prev/next/linear) must not + // carry values across a partition boundary, so the operator compares these + // columns to find where one group ends and the next begins. + TimeWindowPartitionColPos []int32 `protobuf:"varint,74,rep,packed,name=time_window_partition_col_pos,json=timeWindowPartitionColPos,proto3" json:"time_window_partition_col_pos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Node) Reset() { *m = Node{} } @@ -7167,6 +7176,20 @@ func (m *Node) GetDirectView() string { return "" } +func (m *Node) GetTimeWindowPartitionBy() []*Expr { + if m != nil { + return m.TimeWindowPartitionBy + } + return nil +} + +func (m *Node) GetTimeWindowPartitionColPos() []int32 { + if m != nil { + return m.TimeWindowPartitionColPos + } + return nil +} + type SnapshotExtraInfo struct { Level string `protobuf:"bytes,1,opt,name=Level,proto3" json:"Level,omitempty"` ObjId uint64 `protobuf:"varint,2,opt,name=ObjId,proto3" json:"ObjId,omitempty"` @@ -13855,841 +13878,843 @@ func init() { func init() { proto.RegisterFile("plan.proto", fileDescriptor_2d655ab2f7683c23) } var fileDescriptor_2d655ab2f7683c23 = []byte{ - // 13331 bytes of a gzipped FileDescriptorProto + // 13374 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0xbd, 0x5b, 0x8c, 0x23, 0x47, 0x96, 0x18, 0x5a, 0x2c, 0xbe, 0x0f, 0x1f, 0x95, 0x15, 0xfd, 0x62, 0xb7, 0x5a, 0xdd, 0xa5, 0xec, - 0x56, 0xab, 0xd5, 0x92, 0x5a, 0x52, 0xb7, 0x9e, 0xb3, 0xb3, 0x3b, 0xc3, 0x22, 0xd9, 0x5d, 0x94, - 0x58, 0x64, 0x4d, 0x92, 0xd5, 0x2d, 0xcd, 0xde, 0x45, 0x22, 0xc9, 0x4c, 0x56, 0xa5, 0x2a, 0x99, - 0x49, 0x65, 0x26, 0xbb, 0xaa, 0x06, 0x58, 0xdc, 0xc1, 0xfd, 0xd8, 0xc7, 0xc5, 0xfd, 0xbb, 0xf0, - 0xfa, 0x6b, 0xe1, 0xb5, 0x01, 0xc3, 0x80, 0x61, 0xc3, 0xb0, 0x31, 0xc0, 0x7a, 0xff, 0xbc, 0xf0, - 0xcf, 0x1a, 0xb6, 0xd7, 0x0b, 0x1b, 0xb6, 0xb1, 0x36, 0xb0, 0xb6, 0xc7, 0xdf, 0x86, 0x0d, 0xd8, - 0x7f, 0xfe, 0xb0, 0x71, 0x4e, 0x44, 0x66, 0x46, 0x92, 0xec, 0x6e, 0x49, 0x33, 0x0b, 0xf8, 0xa7, - 0x8a, 0x71, 0x1e, 0xf1, 0x8e, 0x13, 0xe7, 0x9c, 0x38, 0x11, 0x09, 0x30, 0x77, 0x0c, 0xf7, 0xfe, - 0xdc, 0xf7, 0x42, 0x8f, 0xe5, 0xf0, 0xf7, 0xb5, 0x77, 0x8e, 0xec, 0xf0, 0x78, 0x31, 0xbe, 0x3f, - 0xf1, 0x66, 0xef, 0x1e, 0x79, 0x47, 0xde, 0xbb, 0x84, 0x1c, 0x2f, 0xa6, 0x94, 0xa2, 0x04, 0xfd, - 0xe2, 0x4c, 0xd7, 0xc0, 0xf1, 0x26, 0x27, 0xe2, 0xf7, 0x56, 0x68, 0xcf, 0xac, 0x20, 0x34, 0x66, - 0x73, 0x0e, 0x50, 0xff, 0x30, 0x03, 0xb9, 0xd1, 0xf9, 0xdc, 0x62, 0x75, 0xd8, 0xb4, 0xcd, 0x46, - 0x66, 0x27, 0x73, 0x37, 0xaf, 0x6d, 0xda, 0x26, 0xdb, 0x81, 0x8a, 0xeb, 0x85, 0xfd, 0x85, 0xe3, - 0x18, 0x63, 0xc7, 0x6a, 0x6c, 0xee, 0x64, 0xee, 0x96, 0x34, 0x19, 0xc4, 0x5e, 0x81, 0xb2, 0xb1, - 0x08, 0x3d, 0xdd, 0x76, 0x27, 0x7e, 0x23, 0x4b, 0xf8, 0x12, 0x02, 0xba, 0xee, 0xc4, 0x67, 0x17, - 0x21, 0x7f, 0x6a, 0x9b, 0xe1, 0x71, 0x23, 0x47, 0x39, 0xf2, 0x04, 0x42, 0x83, 0x89, 0xe1, 0x58, - 0x8d, 0x3c, 0x87, 0x52, 0x02, 0xa1, 0x21, 0x15, 0x52, 0xd8, 0xc9, 0xdc, 0x2d, 0x6b, 0x3c, 0xc1, - 0x6e, 0x00, 0x58, 0xee, 0x62, 0xf6, 0xcc, 0x70, 0x16, 0x56, 0xd0, 0x28, 0x12, 0x4a, 0x82, 0xa8, - 0x3f, 0x80, 0xf2, 0x2c, 0x38, 0xda, 0xb3, 0x0c, 0xd3, 0xf2, 0xd9, 0x15, 0x28, 0xce, 0x82, 0x23, - 0x3d, 0x34, 0x8e, 0x44, 0x13, 0x0a, 0xb3, 0xe0, 0x68, 0x64, 0x1c, 0xb1, 0xab, 0x50, 0x22, 0xc4, - 0xf9, 0x9c, 0xb7, 0x21, 0xaf, 0x21, 0x21, 0xb6, 0x58, 0xfd, 0xdd, 0x02, 0x14, 0x7b, 0x76, 0x68, - 0xf9, 0x86, 0xc3, 0x2e, 0x43, 0xc1, 0x0e, 0xdc, 0x85, 0xe3, 0x10, 0x7b, 0x49, 0x13, 0x29, 0x76, - 0x19, 0xf2, 0xf6, 0x27, 0xcf, 0x0c, 0x87, 0xf3, 0xee, 0x6d, 0x68, 0x3c, 0xc9, 0x1a, 0x50, 0xb0, - 0xdf, 0xff, 0x08, 0x11, 0x59, 0x81, 0x10, 0x69, 0xc2, 0x3c, 0x7c, 0x80, 0x98, 0x5c, 0x8c, 0xa1, - 0x34, 0x61, 0x3e, 0xfa, 0x00, 0x31, 0xd8, 0xfa, 0x2c, 0x61, 0x28, 0x8d, 0xa5, 0x2c, 0xa8, 0x14, - 0xec, 0x80, 0x1a, 0x96, 0xb2, 0x88, 0x4a, 0x59, 0xf0, 0x52, 0x8a, 0x02, 0x21, 0xd2, 0x84, 0xe1, - 0xa5, 0x94, 0x62, 0x4c, 0x5c, 0xca, 0x82, 0x97, 0x52, 0xde, 0xc9, 0xdc, 0xcd, 0x11, 0x86, 0x97, - 0x72, 0x11, 0x72, 0x26, 0xc2, 0x61, 0x27, 0x73, 0x37, 0xb3, 0xb7, 0xa1, 0x51, 0x0a, 0xa1, 0x01, - 0x42, 0x2b, 0xd8, 0xc1, 0x08, 0x0d, 0x04, 0x74, 0x8c, 0xd0, 0x2a, 0xf6, 0x06, 0x42, 0xc7, 0x02, - 0x3a, 0x45, 0x68, 0x6d, 0x27, 0x73, 0x77, 0x13, 0xa1, 0x98, 0x62, 0xd7, 0xa0, 0x68, 0x1a, 0xa1, - 0x85, 0x88, 0xba, 0x68, 0x72, 0x04, 0x40, 0x1c, 0xce, 0x38, 0xc4, 0x6d, 0x89, 0x46, 0x47, 0x00, - 0xa6, 0x42, 0x05, 0xc9, 0x22, 0xbc, 0x22, 0xf0, 0x32, 0x90, 0x7d, 0x08, 0x55, 0xd3, 0x9a, 0xd8, - 0x33, 0xc3, 0xe1, 0x6d, 0xda, 0xde, 0xc9, 0xdc, 0xad, 0x3c, 0xd8, 0xba, 0x4f, 0x6b, 0x22, 0xc6, - 0xec, 0x6d, 0x68, 0x29, 0x32, 0xf6, 0x09, 0xd4, 0x44, 0xfa, 0xfd, 0x07, 0xd4, 0xb1, 0x8c, 0xf8, - 0x94, 0x14, 0xdf, 0xfb, 0x0f, 0x3e, 0xd9, 0xdb, 0xd0, 0xd2, 0x84, 0xec, 0x36, 0x54, 0xe3, 0x25, - 0x82, 0x8c, 0x17, 0x44, 0xad, 0x52, 0x50, 0x6c, 0xd6, 0x57, 0x81, 0xe7, 0x22, 0xc1, 0x45, 0xd1, - 0x6f, 0x11, 0x80, 0xed, 0x00, 0x98, 0xd6, 0xd4, 0x58, 0x38, 0x21, 0xa2, 0x2f, 0x89, 0x0e, 0x94, - 0x60, 0xec, 0x06, 0x94, 0x17, 0x73, 0x6c, 0xe5, 0x13, 0xc3, 0x69, 0x5c, 0x16, 0x04, 0x09, 0x08, - 0x73, 0xc7, 0x79, 0x8e, 0xd8, 0x2b, 0x62, 0x74, 0x23, 0x00, 0x0e, 0xef, 0x33, 0x6b, 0x82, 0xa8, - 0x86, 0x28, 0x58, 0xa4, 0x71, 0x15, 0xd9, 0xc1, 0xae, 0xed, 0x36, 0xae, 0xd2, 0x0c, 0xe6, 0x09, - 0x76, 0x1d, 0xb2, 0x81, 0x3f, 0x69, 0x5c, 0xa3, 0xf6, 0x03, 0x6f, 0x7f, 0xe7, 0x6c, 0xee, 0x6b, - 0x08, 0xde, 0x2d, 0x42, 0x9e, 0x56, 0x93, 0x7a, 0x1d, 0x4a, 0x07, 0x86, 0x6f, 0xcc, 0x34, 0x6b, - 0xca, 0x14, 0xc8, 0xce, 0xbd, 0x40, 0xac, 0x23, 0xfc, 0xa9, 0xf6, 0xa0, 0xf0, 0xc4, 0xf0, 0x11, - 0xc7, 0x20, 0xe7, 0x1a, 0x33, 0x8b, 0x90, 0x65, 0x8d, 0x7e, 0xe3, 0xda, 0x09, 0xce, 0x83, 0xd0, - 0x9a, 0x09, 0x21, 0x21, 0x52, 0x08, 0x3f, 0x72, 0xbc, 0xb1, 0x58, 0x23, 0x25, 0x4d, 0xa4, 0xd4, - 0xff, 0x27, 0x03, 0x85, 0x96, 0xe7, 0x60, 0x76, 0x57, 0xa0, 0xe8, 0x5b, 0x8e, 0x9e, 0x14, 0x57, - 0xf0, 0x2d, 0xe7, 0xc0, 0x0b, 0x10, 0x31, 0xf1, 0x38, 0x82, 0xaf, 0xda, 0xc2, 0xc4, 0x23, 0x44, - 0x54, 0x81, 0xac, 0x54, 0x81, 0xab, 0x50, 0x0a, 0xc7, 0x8e, 0x4e, 0xf0, 0x1c, 0xc1, 0x8b, 0xe1, - 0xd8, 0xe9, 0x23, 0xea, 0x0a, 0x14, 0xcd, 0x31, 0xc7, 0xe4, 0x09, 0x53, 0x30, 0xc7, 0x88, 0x50, - 0x3f, 0x85, 0xb2, 0x66, 0x9c, 0x8a, 0x6a, 0x5c, 0x82, 0x02, 0x66, 0x20, 0xe4, 0x5f, 0x4e, 0xcb, - 0x87, 0x63, 0xa7, 0x6b, 0x22, 0x18, 0x2b, 0x61, 0x9b, 0x54, 0x87, 0x9c, 0x96, 0x9f, 0x78, 0x4e, - 0xd7, 0x54, 0x47, 0x00, 0x2d, 0xcf, 0xf7, 0xbf, 0x73, 0x13, 0x2e, 0x42, 0xde, 0xb4, 0xe6, 0xe1, - 0x31, 0x17, 0x1d, 0x1a, 0x4f, 0xa8, 0xf7, 0xa0, 0x84, 0xe3, 0xd2, 0xb3, 0x83, 0x90, 0xdd, 0x80, - 0x9c, 0x63, 0x07, 0x61, 0x23, 0xb3, 0x93, 0x5d, 0x1a, 0x35, 0x82, 0xab, 0x3b, 0x50, 0xda, 0x37, - 0xce, 0x9e, 0xe0, 0xc8, 0x61, 0x6e, 0x34, 0x84, 0x62, 0x48, 0xc4, 0x78, 0x56, 0x01, 0x46, 0x86, - 0x7f, 0x64, 0x85, 0x24, 0xe9, 0xfe, 0x7b, 0x06, 0x2a, 0xc3, 0xc5, 0xf8, 0xeb, 0x85, 0xe5, 0x9f, - 0x63, 0x9d, 0xef, 0x42, 0x36, 0x3c, 0x9f, 0x13, 0x47, 0xfd, 0xc1, 0x65, 0x9e, 0xbd, 0x84, 0xbf, - 0x8f, 0x4c, 0x1a, 0x92, 0x60, 0x23, 0x5c, 0xcf, 0xb4, 0xa2, 0x3e, 0xc8, 0x6b, 0x05, 0x4c, 0x76, - 0x4d, 0xdc, 0x2e, 0xbc, 0xb9, 0x18, 0x85, 0x4d, 0x6f, 0xce, 0x76, 0x20, 0x3f, 0x39, 0xb6, 0x1d, - 0x93, 0x06, 0x20, 0x5d, 0x67, 0x8e, 0xc0, 0x51, 0xf2, 0xbd, 0x53, 0x3d, 0xb0, 0x7f, 0x12, 0x89, - 0xff, 0xa2, 0xef, 0x9d, 0x0e, 0xed, 0x9f, 0x58, 0xea, 0x48, 0xec, 0x41, 0x00, 0x85, 0x61, 0xab, - 0xd9, 0x6b, 0x6a, 0xca, 0x06, 0xfe, 0xee, 0x7c, 0xd1, 0x1d, 0x8e, 0x86, 0x4a, 0x86, 0xd5, 0x01, - 0xfa, 0x83, 0x91, 0x2e, 0xd2, 0x9b, 0xac, 0x00, 0x9b, 0xdd, 0xbe, 0x92, 0x45, 0x1a, 0x84, 0x77, - 0xfb, 0x4a, 0x8e, 0x15, 0x21, 0xdb, 0xec, 0x7f, 0xa9, 0xe4, 0xe9, 0x47, 0xaf, 0xa7, 0x14, 0xd4, - 0x3f, 0xdd, 0x84, 0xf2, 0x60, 0xfc, 0x95, 0x35, 0x09, 0xb1, 0xcd, 0x38, 0x4b, 0x2d, 0xff, 0x99, - 0xe5, 0x53, 0xb3, 0xb3, 0x9a, 0x48, 0x61, 0x43, 0xcc, 0x31, 0x35, 0x2e, 0xab, 0x6d, 0x9a, 0x63, - 0xa2, 0x9b, 0x1c, 0x5b, 0x33, 0x83, 0x1a, 0x87, 0x74, 0x94, 0xc2, 0x55, 0xe1, 0x8d, 0xbf, 0xa2, - 0xe6, 0x65, 0x35, 0xfc, 0xc9, 0x6e, 0x42, 0x85, 0xe7, 0x21, 0xcf, 0x2f, 0xe0, 0xa0, 0xe5, 0xc9, - 0x57, 0x90, 0x27, 0x1f, 0x71, 0x52, 0xae, 0x1c, 0x29, 0xf6, 0x36, 0x0e, 0xea, 0x8b, 0x19, 0xed, - 0x8d, 0xbf, 0xe2, 0xd8, 0x12, 0x9f, 0xd1, 0xde, 0xf8, 0x2b, 0x42, 0xbd, 0x05, 0xdb, 0xc1, 0x62, - 0x1c, 0x4c, 0x7c, 0x7b, 0x1e, 0xda, 0x9e, 0xcb, 0x69, 0xca, 0x44, 0xa3, 0xc8, 0x08, 0x22, 0xbe, - 0x0b, 0xa5, 0xf9, 0x62, 0xac, 0xdb, 0xee, 0xd4, 0x23, 0xb1, 0x5f, 0x79, 0x50, 0xe3, 0x03, 0x73, - 0xb0, 0x18, 0x77, 0xdd, 0xa9, 0xa7, 0x15, 0xe7, 0xfc, 0x07, 0x53, 0xa1, 0xe6, 0x7a, 0xa1, 0x8e, - 0xaa, 0x82, 0x3e, 0xb3, 0x42, 0x83, 0xf6, 0x03, 0xbe, 0xe1, 0xf7, 0xbc, 0xc9, 0xc9, 0xbe, 0x15, - 0x1a, 0xea, 0x1d, 0x28, 0x0a, 0x3e, 0xdc, 0xfb, 0x43, 0xcb, 0x35, 0xdc, 0x50, 0x8f, 0x95, 0x86, - 0x12, 0x07, 0x74, 0x4d, 0xf5, 0x67, 0x19, 0x50, 0x86, 0x52, 0x55, 0x90, 0x79, 0xad, 0xe4, 0x78, - 0x15, 0xc0, 0x98, 0x4c, 0xbc, 0x05, 0xcf, 0x86, 0x4f, 0xb0, 0xb2, 0x80, 0x74, 0x4d, 0xb9, 0xff, - 0xb2, 0xa9, 0xfe, 0x7b, 0x0d, 0xaa, 0x11, 0x9f, 0xb4, 0xe8, 0x2b, 0x02, 0x16, 0xf5, 0x60, 0xb0, - 0x48, 0xad, 0xfc, 0x62, 0xb0, 0xe0, 0xdc, 0x97, 0xa1, 0x40, 0x1a, 0x46, 0x10, 0x8d, 0x0a, 0x4f, - 0xa9, 0xff, 0x36, 0x03, 0xb5, 0xae, 0x6b, 0x5a, 0x67, 0xc3, 0x89, 0xe1, 0x46, 0x9d, 0x62, 0x07, - 0xba, 0x8d, 0x30, 0x3d, 0x98, 0x18, 0xae, 0x50, 0x0e, 0x2a, 0x76, 0x10, 0xd3, 0x61, 0x1b, 0x38, - 0x01, 0x15, 0xb5, 0x49, 0x39, 0x96, 0x09, 0x42, 0x85, 0xdd, 0x81, 0xad, 0xb1, 0xe5, 0x78, 0xee, - 0x91, 0x1e, 0x7a, 0x3a, 0xd7, 0x72, 0x78, 0x5b, 0x6a, 0x1c, 0x3c, 0xf2, 0x46, 0xa4, 0xed, 0x5c, - 0x84, 0xfc, 0xdc, 0xf0, 0xc3, 0xa0, 0x91, 0xdb, 0xc9, 0xe2, 0x32, 0xa6, 0x04, 0x76, 0xb3, 0x1d, - 0xe8, 0x0b, 0xd7, 0xfe, 0x7a, 0xc1, 0x9b, 0x51, 0xd2, 0x4a, 0x76, 0x70, 0x48, 0x69, 0x76, 0x17, - 0x14, 0x5e, 0x32, 0x65, 0x2b, 0xcf, 0xb3, 0x3a, 0xc1, 0x29, 0x63, 0x12, 0x76, 0xff, 0xef, 0x26, - 0x94, 0x1e, 0x2d, 0xdc, 0x09, 0x0e, 0x06, 0xbb, 0x05, 0xb9, 0xe9, 0xc2, 0x9d, 0x50, 0x5b, 0xe2, - 0xad, 0x34, 0x5e, 0x27, 0x1a, 0x21, 0x51, 0x02, 0x19, 0xfe, 0x11, 0x4a, 0xae, 0x15, 0x09, 0x84, - 0x70, 0xf5, 0x1f, 0x66, 0x78, 0x8e, 0x8f, 0x1c, 0xe3, 0x88, 0x95, 0x20, 0xd7, 0x1f, 0xf4, 0x3b, - 0xca, 0x06, 0xab, 0x42, 0xa9, 0xdb, 0x1f, 0x75, 0xb4, 0x7e, 0xb3, 0xa7, 0x64, 0x68, 0x39, 0x8f, - 0x9a, 0xbb, 0xbd, 0x8e, 0xb2, 0x89, 0x98, 0x27, 0x83, 0x5e, 0x73, 0xd4, 0xed, 0x75, 0x94, 0x1c, - 0xc7, 0x68, 0xdd, 0xd6, 0x48, 0x29, 0x31, 0x05, 0xaa, 0x07, 0xda, 0xa0, 0x7d, 0xd8, 0xea, 0xe8, - 0xfd, 0xc3, 0x5e, 0x4f, 0x51, 0xd8, 0x05, 0xd8, 0x8a, 0x21, 0x03, 0x0e, 0xdc, 0x41, 0x96, 0x27, - 0x4d, 0xad, 0xa9, 0x3d, 0x56, 0x7e, 0xc8, 0x4a, 0x90, 0x6d, 0x3e, 0x7e, 0xac, 0xfc, 0x14, 0x25, - 0x43, 0xf9, 0x69, 0xb7, 0xaf, 0x3f, 0x69, 0xf6, 0x0e, 0x3b, 0xca, 0x4f, 0x37, 0xa3, 0xf4, 0x40, - 0x6b, 0x77, 0x34, 0xe5, 0xa7, 0x39, 0xb6, 0x0d, 0xd5, 0x1f, 0x0f, 0xfa, 0x9d, 0xfd, 0xe6, 0xc1, - 0x01, 0x55, 0xe4, 0xa7, 0x25, 0xf5, 0xbf, 0xe4, 0x20, 0x87, 0x2d, 0x61, 0x6a, 0x22, 0x05, 0xe3, - 0x26, 0xa2, 0x18, 0xda, 0xcd, 0xfd, 0xc9, 0x5f, 0xdc, 0xdc, 0xe0, 0xf2, 0xef, 0x35, 0xc8, 0x3a, - 0x76, 0x48, 0xc3, 0x1a, 0xaf, 0x1d, 0xa1, 0x33, 0xee, 0x6d, 0x68, 0x88, 0x63, 0x37, 0x20, 0xc3, - 0x05, 0x61, 0xe5, 0x41, 0x5d, 0x2c, 0x2e, 0xb1, 0x93, 0xee, 0x6d, 0x68, 0x99, 0x39, 0xbb, 0x0e, - 0x99, 0x67, 0x42, 0x2a, 0x56, 0x39, 0x9e, 0xef, 0xa5, 0x88, 0x7d, 0xc6, 0x76, 0x20, 0x3b, 0xf1, - 0xb8, 0x46, 0x18, 0xe3, 0xf9, 0xce, 0x82, 0xf9, 0x4f, 0x3c, 0x87, 0xdd, 0x82, 0xac, 0x6f, 0x9c, - 0xd2, 0xc8, 0xc6, 0xc3, 0x15, 0x6f, 0x5d, 0x48, 0xe4, 0x1b, 0xa7, 0x58, 0x89, 0x29, 0xc9, 0x91, - 0xb8, 0x12, 0xd1, 0x78, 0x63, 0x31, 0x53, 0xb6, 0x03, 0x99, 0x53, 0x92, 0x24, 0xb1, 0x12, 0xf4, - 0xd4, 0x76, 0x4d, 0xef, 0x74, 0x38, 0xb7, 0x26, 0x48, 0x71, 0xca, 0x5e, 0x87, 0x6c, 0xb0, 0x18, - 0x93, 0x24, 0xa9, 0x3c, 0xd8, 0x5e, 0xd9, 0x13, 0xb0, 0xa0, 0x60, 0x31, 0x66, 0x77, 0x20, 0x37, - 0xf1, 0x7c, 0x5f, 0x48, 0x13, 0x25, 0xaa, 0x70, 0xb4, 0x1d, 0xa2, 0x52, 0x88, 0x78, 0x2c, 0x30, - 0x24, 0x19, 0x12, 0x13, 0x25, 0xfb, 0x11, 0x16, 0x18, 0xb2, 0xdb, 0x62, 0x93, 0xab, 0xca, 0xb5, - 0x8e, 0xb6, 0x40, 0xcc, 0x07, 0xb1, 0x38, 0x48, 0x33, 0xe3, 0x8c, 0x34, 0xce, 0x98, 0x28, 0xda, - 0xfb, 0xb0, 0x4e, 0x33, 0xe3, 0x8c, 0xdd, 0x86, 0xec, 0x33, 0x6b, 0x42, 0xca, 0x67, 0x5c, 0x9a, - 0x18, 0xa4, 0x27, 0xd4, 0x3c, 0x44, 0xd3, 0xbc, 0xf7, 0x1c, 0x93, 0xf4, 0xd0, 0x78, 0x2c, 0x1f, - 0x79, 0x8e, 0xf9, 0x84, 0xc6, 0x92, 0x90, 0xb8, 0xe5, 0x1b, 0x8b, 0x33, 0x94, 0x46, 0x0a, 0xdf, - 0x9c, 0x8d, 0xc5, 0x59, 0xd7, 0x44, 0xe1, 0xef, 0x9a, 0xcf, 0x48, 0xfb, 0xcc, 0x68, 0xf8, 0x13, - 0xcd, 0xa3, 0xc0, 0x72, 0xac, 0x49, 0x68, 0x3f, 0xb3, 0xc3, 0x73, 0xd2, 0x2f, 0x33, 0x9a, 0x0c, - 0xda, 0x2d, 0x40, 0xce, 0x3a, 0x9b, 0xfb, 0xea, 0x1e, 0x14, 0x45, 0x29, 0x2b, 0x36, 0xd6, 0x55, - 0x28, 0xd9, 0x81, 0x3e, 0xf1, 0xdc, 0x20, 0x14, 0xba, 0x53, 0xd1, 0x0e, 0x5a, 0x98, 0x44, 0x71, - 0x69, 0x1a, 0x21, 0xdf, 0x84, 0xaa, 0x1a, 0xfd, 0x56, 0x1f, 0x00, 0x24, 0xcd, 0xc2, 0x3a, 0x39, - 0x96, 0x1b, 0xa9, 0x69, 0x8e, 0xe5, 0xc6, 0x3c, 0x9b, 0x12, 0xcf, 0x55, 0x28, 0xc7, 0x9a, 0x31, - 0xab, 0x42, 0xc6, 0x10, 0xdb, 0x5f, 0xc6, 0x50, 0xef, 0xa2, 0xa2, 0x1a, 0xe9, 0xbe, 0x69, 0x1c, - 0xa6, 0xa2, 0x4d, 0x31, 0x33, 0x56, 0xbf, 0x0f, 0x55, 0xcd, 0x0a, 0x16, 0x4e, 0xd8, 0xf2, 0x9c, - 0xb6, 0x35, 0x65, 0x6f, 0x03, 0xc4, 0xe9, 0x40, 0x68, 0x29, 0xc9, 0xdc, 0x6d, 0x5b, 0x53, 0x4d, - 0xc2, 0xab, 0xff, 0x29, 0x47, 0xfa, 0x5e, 0x9b, 0x2b, 0x5a, 0x42, 0xa3, 0xca, 0x48, 0x1a, 0x55, - 0xbc, 0x37, 0x6c, 0xa6, 0xb5, 0xca, 0x63, 0xdb, 0x34, 0x2d, 0x37, 0xd2, 0x1e, 0x79, 0x0a, 0x07, - 0xdb, 0x70, 0x8e, 0x68, 0x41, 0xd5, 0x1f, 0xb0, 0xa8, 0xd0, 0xd9, 0xdc, 0xb7, 0x82, 0x80, 0xeb, - 0x2d, 0x86, 0x73, 0x14, 0xad, 0xed, 0xfc, 0x8b, 0xd6, 0xf6, 0x55, 0x28, 0xe1, 0x96, 0x47, 0x56, - 0x5f, 0x81, 0xf7, 0xbe, 0x30, 0x6f, 0xd9, 0x1b, 0x50, 0x14, 0xfa, 0xba, 0x58, 0x54, 0x62, 0xba, - 0xb4, 0x39, 0x50, 0x8b, 0xb0, 0xac, 0x81, 0x4a, 0xde, 0x6c, 0x66, 0xb9, 0x61, 0xb4, 0x4f, 0x8b, - 0x24, 0x7b, 0x0b, 0xca, 0x9e, 0xab, 0x73, 0xa5, 0x5e, 0xac, 0x2a, 0x31, 0x7d, 0x07, 0xee, 0x21, - 0x41, 0xb5, 0x92, 0x27, 0x7e, 0x61, 0x55, 0x1c, 0xef, 0x54, 0x9f, 0x18, 0xbe, 0x49, 0x2b, 0xab, - 0xa4, 0x15, 0x1d, 0xef, 0xb4, 0x65, 0xf8, 0x26, 0xd7, 0x5b, 0xbe, 0x76, 0x17, 0x33, 0x5a, 0x4d, - 0x35, 0x4d, 0xa4, 0xd8, 0x75, 0x28, 0x4f, 0x9c, 0x45, 0x10, 0x5a, 0xfe, 0xee, 0x39, 0x37, 0xd3, - 0xb4, 0x04, 0x80, 0xf5, 0x9a, 0xfb, 0xf6, 0xcc, 0xf0, 0xcf, 0x69, 0xe9, 0x94, 0xb4, 0x28, 0x49, - 0x1b, 0xcd, 0x89, 0x6d, 0x9e, 0x71, 0x5b, 0x4d, 0xe3, 0x09, 0xa4, 0x3f, 0x26, 0x4b, 0x3a, 0xa0, - 0xf5, 0x51, 0xd2, 0xa2, 0x24, 0x8d, 0x03, 0xfd, 0xa4, 0x15, 0x51, 0xd6, 0x44, 0x2a, 0xa5, 0x74, - 0x6f, 0x3f, 0x57, 0xe9, 0x66, 0xcb, 0x7a, 0x8f, 0xe7, 0xdb, 0x47, 0xb6, 0xd0, 0x5a, 0x2e, 0x70, - 0xbd, 0x87, 0x83, 0x88, 0xe0, 0x63, 0xa8, 0x1d, 0x59, 0xae, 0xe5, 0x1b, 0xa1, 0x65, 0xea, 0x28, - 0x17, 0x2f, 0x52, 0xc7, 0x89, 0x61, 0x7e, 0x1c, 0xa1, 0x50, 0xd6, 0x54, 0x8f, 0xa4, 0x94, 0xfa, - 0x35, 0x14, 0xc5, 0xd8, 0xe0, 0xd6, 0x85, 0xeb, 0x2e, 0x2d, 0xd7, 0xf9, 0xd6, 0x85, 0x70, 0x76, - 0x0b, 0x6a, 0xa2, 0x12, 0x41, 0xe8, 0xdb, 0xee, 0x91, 0x98, 0x75, 0x55, 0x0e, 0x1c, 0x12, 0x0c, - 0x35, 0x0c, 0x9c, 0x17, 0xba, 0x31, 0xb6, 0x1d, 0x5c, 0xdf, 0x59, 0xa1, 0x0d, 0x2d, 0x1c, 0xa7, - 0xc9, 0x41, 0xea, 0x00, 0x4a, 0xd1, 0x48, 0xfe, 0x52, 0xca, 0x54, 0xe7, 0x50, 0x95, 0x5b, 0xf8, - 0xcb, 0x69, 0x08, 0xd7, 0x20, 0x82, 0xd0, 0xf3, 0x2d, 0x33, 0x72, 0xd2, 0xd8, 0xc1, 0x90, 0xd2, - 0xea, 0x6f, 0x65, 0xa0, 0x42, 0x9a, 0xcc, 0x80, 0xf4, 0x34, 0xf6, 0x36, 0xb0, 0x89, 0x6f, 0x19, - 0xa1, 0xa5, 0x5b, 0x67, 0xa1, 0x6f, 0x08, 0x7d, 0x85, 0x2b, 0x3d, 0x0a, 0xc7, 0x74, 0x10, 0xc1, - 0x55, 0x96, 0x9b, 0x50, 0x99, 0x1b, 0x7e, 0x10, 0xe9, 0xbf, 0xbc, 0x74, 0xe0, 0x20, 0xa1, 0x7d, - 0x2a, 0xee, 0x91, 0x6f, 0xcc, 0xf4, 0xd0, 0x3b, 0xb1, 0x5c, 0xae, 0xf9, 0x73, 0x9b, 0xa7, 0x4e, - 0xf0, 0x11, 0x82, 0xc9, 0x00, 0xf8, 0x77, 0x19, 0xa8, 0x1d, 0xf0, 0x09, 0xfa, 0xb9, 0x75, 0xde, - 0xe6, 0x86, 0xe6, 0x24, 0x12, 0x2e, 0x39, 0x8d, 0x7e, 0xb3, 0x1b, 0x50, 0x99, 0x9f, 0x58, 0xe7, - 0x7a, 0xca, 0x28, 0x2b, 0x23, 0xa8, 0x45, 0x62, 0xe4, 0x4d, 0x28, 0x78, 0xd4, 0x10, 0xb1, 0x1d, - 0x8b, 0x5d, 0x4c, 0x6a, 0xa1, 0x26, 0x08, 0x50, 0xb3, 0x8b, 0xb3, 0x92, 0x55, 0x48, 0x91, 0x19, - 0x55, 0xff, 0x22, 0xe4, 0x11, 0x15, 0x34, 0xf2, 0x5c, 0x25, 0xa3, 0x04, 0x7b, 0x0f, 0x6a, 0x13, - 0x6f, 0x36, 0xd7, 0x23, 0x76, 0xb1, 0x31, 0xa7, 0xc5, 0x5f, 0x05, 0x49, 0x0e, 0x78, 0x5e, 0xea, - 0xef, 0x65, 0xa1, 0x44, 0x75, 0x10, 0x12, 0xd0, 0x36, 0xcf, 0x22, 0x09, 0x58, 0xd6, 0xf2, 0xb6, - 0x89, 0x1b, 0xcc, 0x4b, 0xb4, 0xc8, 0x58, 0x3b, 0xcc, 0xca, 0xda, 0xe1, 0x65, 0x28, 0x08, 0xd5, - 0x30, 0xc7, 0x45, 0xe4, 0xe2, 0xf9, 0x8a, 0x61, 0x7e, 0x9d, 0x62, 0x88, 0x43, 0xc8, 0x69, 0xac, - 0x33, 0xdc, 0x8a, 0xb9, 0x14, 0x04, 0x02, 0x75, 0x10, 0x22, 0xcb, 0xb7, 0x62, 0x5a, 0xbe, 0x35, - 0xa0, 0xf8, 0xcc, 0x0e, 0x6c, 0x9c, 0x20, 0x25, 0x2e, 0x31, 0x44, 0x52, 0x1a, 0x86, 0xf2, 0xcb, - 0x86, 0x21, 0x6e, 0xb6, 0xe1, 0x1c, 0x71, 0x0b, 0x25, 0x6a, 0x76, 0xd3, 0x39, 0xf2, 0xd8, 0xfb, - 0x70, 0x29, 0x41, 0x8b, 0xd6, 0x90, 0x27, 0x8f, 0x9c, 0x55, 0x1a, 0x8b, 0x29, 0xa9, 0x45, 0x64, - 0x42, 0xde, 0x83, 0x6d, 0x89, 0x65, 0x8e, 0x9a, 0x58, 0x40, 0xe2, 0xb1, 0xac, 0x6d, 0xc5, 0xe4, - 0xa4, 0xa0, 0x05, 0xea, 0x3f, 0xd9, 0x84, 0xda, 0x23, 0xcf, 0xb7, 0xec, 0x23, 0x37, 0x99, 0x75, - 0x2b, 0x46, 0x4a, 0x34, 0x13, 0x37, 0xa5, 0x99, 0x78, 0x13, 0x2a, 0x53, 0xce, 0xa8, 0x87, 0x63, - 0xee, 0xdf, 0xc8, 0x69, 0x20, 0x40, 0xa3, 0xb1, 0x83, 0xf2, 0x23, 0x22, 0x20, 0xe6, 0x1c, 0x31, - 0x47, 0x4c, 0xb8, 0x2d, 0xb2, 0xef, 0xd1, 0x06, 0x61, 0x5a, 0x8e, 0x15, 0xf2, 0xe1, 0xa9, 0x3f, - 0x78, 0x35, 0x52, 0x4a, 0xa4, 0x3a, 0xdd, 0xd7, 0xac, 0x69, 0x93, 0x34, 0x39, 0xdc, 0x2f, 0xda, - 0x44, 0x2e, 0x78, 0xc5, 0xe6, 0x52, 0xf8, 0x86, 0xbc, 0x5c, 0x56, 0xa9, 0x23, 0x28, 0xc7, 0x60, - 0x54, 0xcb, 0xb5, 0x8e, 0x50, 0xc5, 0x37, 0x58, 0x05, 0x8a, 0xad, 0xe6, 0xb0, 0xd5, 0x6c, 0x77, - 0x94, 0x0c, 0xa2, 0x86, 0x9d, 0x11, 0x57, 0xbf, 0x37, 0xd9, 0x16, 0x54, 0x30, 0xd5, 0xee, 0x3c, - 0x6a, 0x1e, 0xf6, 0x46, 0x4a, 0x96, 0xd5, 0xa0, 0xdc, 0x1f, 0xe8, 0xcd, 0xd6, 0xa8, 0x3b, 0xe8, - 0x2b, 0x39, 0xf5, 0x87, 0x50, 0x6a, 0x1d, 0x5b, 0x93, 0x93, 0xe7, 0xf5, 0x22, 0xf9, 0x07, 0xac, - 0xc9, 0x89, 0x50, 0xa5, 0x97, 0xfc, 0x03, 0xd6, 0xe4, 0x44, 0xed, 0x40, 0xf9, 0xc0, 0xf0, 0x43, - 0x9b, 0xea, 0xf5, 0x09, 0xd4, 0xe2, 0x44, 0xdb, 0x9a, 0x46, 0x4a, 0x06, 0x8b, 0x15, 0xec, 0x18, - 0xa5, 0xa5, 0x09, 0xd5, 0xb7, 0xa1, 0x2a, 0x03, 0xd8, 0x75, 0xc8, 0x9a, 0xd6, 0x74, 0x8d, 0x10, - 0x45, 0xb0, 0xfa, 0x04, 0xaa, 0xad, 0x68, 0xd3, 0x7c, 0x5e, 0xd5, 0x1f, 0x40, 0x9d, 0x56, 0xfc, - 0x64, 0x1c, 0x2d, 0xf9, 0xcd, 0x35, 0x4b, 0xbe, 0x8a, 0x34, 0xad, 0xb1, 0x58, 0xf3, 0x1f, 0x42, - 0xe5, 0xc0, 0xf7, 0xe6, 0x96, 0x1f, 0x52, 0xb6, 0x0a, 0x64, 0x4f, 0xac, 0x73, 0x91, 0x2b, 0xfe, - 0x4c, 0xdc, 0x36, 0x9b, 0xb2, 0xdb, 0xe6, 0x01, 0x94, 0x22, 0xb6, 0x6f, 0xcc, 0xf3, 0x03, 0x14, - 0x9d, 0xc4, 0x63, 0x5b, 0x01, 0x16, 0x76, 0x1f, 0x60, 0x1e, 0x03, 0x44, 0xc7, 0x45, 0x96, 0x89, - 0xc8, 0x5c, 0x93, 0x28, 0xd4, 0x57, 0xa1, 0xf8, 0xc4, 0xb6, 0x4e, 0x45, 0xf3, 0x9f, 0xd9, 0xd6, - 0x69, 0xd4, 0x7c, 0xfc, 0xad, 0xfe, 0x8b, 0x32, 0x94, 0x68, 0x7d, 0xb5, 0x9f, 0xef, 0x29, 0xfb, - 0x36, 0x0a, 0xdc, 0x8e, 0x58, 0x4f, 0xb9, 0x35, 0x6a, 0x23, 0x5f, 0x5d, 0xaf, 0x02, 0x48, 0x6b, - 0x9d, 0x4b, 0xae, 0x72, 0x18, 0x2f, 0x71, 0xd4, 0x7c, 0x68, 0x2f, 0x0a, 0xbe, 0x76, 0x84, 0xc1, - 0x9b, 0x00, 0xd8, 0x7d, 0xae, 0x97, 0x90, 0x89, 0xcb, 0x75, 0xb7, 0x0b, 0x91, 0xfd, 0x31, 0x76, - 0xac, 0xc8, 0x2a, 0x22, 0x65, 0x05, 0x13, 0x24, 0xc7, 0x2c, 0x3f, 0x40, 0x71, 0x45, 0xae, 0x74, - 0x2d, 0x4a, 0xb2, 0x37, 0x20, 0x87, 0x42, 0x5e, 0x58, 0x31, 0x17, 0xa2, 0x1e, 0x94, 0x76, 0x29, - 0x8d, 0x08, 0xd8, 0x5d, 0x28, 0x92, 0x68, 0xb1, 0x50, 0xd2, 0x48, 0xbd, 0x1d, 0x09, 0x7d, 0x2d, - 0x42, 0xb3, 0x37, 0x21, 0x3f, 0x3d, 0xb1, 0xce, 0x83, 0x46, 0x8d, 0xe8, 0x2e, 0xac, 0x59, 0xb3, - 0x1a, 0xa7, 0x60, 0xb7, 0xa1, 0xee, 0x5b, 0x53, 0x9d, 0x7c, 0x67, 0x28, 0x64, 0x82, 0x46, 0x9d, - 0x64, 0x48, 0xd5, 0xb7, 0xa6, 0x2d, 0x04, 0x8e, 0xc6, 0x4e, 0xc0, 0xee, 0x40, 0x81, 0x56, 0x0f, - 0xaa, 0x6d, 0x52, 0xc9, 0xd1, 0x52, 0xd4, 0x04, 0x96, 0xbd, 0x0f, 0x20, 0x94, 0x43, 0x7d, 0x7c, - 0x4e, 0x3e, 0xe7, 0x78, 0x31, 0xc9, 0xf3, 0x5f, 0x56, 0x21, 0xdf, 0x80, 0x3c, 0x4e, 0x92, 0xa0, - 0x71, 0x85, 0x72, 0xde, 0x4e, 0xcf, 0x20, 0xaa, 0x29, 0xe1, 0xd9, 0x5d, 0x28, 0xe1, 0x44, 0xd1, - 0x71, 0x38, 0x1a, 0xb2, 0xb6, 0x2c, 0x66, 0x15, 0xee, 0x0c, 0xd6, 0xe9, 0xf0, 0x6b, 0x87, 0xdd, - 0x83, 0x9c, 0x89, 0x8b, 0xf9, 0x2a, 0xe5, 0x78, 0x59, 0x1a, 0x17, 0x14, 0x56, 0x6d, 0x6b, 0x4a, - 0x0a, 0x3c, 0xd1, 0xb0, 0x3d, 0xa8, 0xe3, 0x34, 0x7a, 0x40, 0x9b, 0x3d, 0x76, 0x5f, 0xe3, 0x1a, - 0x71, 0xbd, 0xb6, 0xc4, 0xd5, 0x17, 0x44, 0xd4, 0xd9, 0x1d, 0x37, 0xf4, 0xcf, 0xb5, 0x9a, 0x2b, - 0xc3, 0xd8, 0x35, 0xb4, 0xb2, 0x7a, 0xde, 0xe4, 0xc4, 0x32, 0x1b, 0xaf, 0x44, 0x1a, 0x10, 0x4f, - 0xb3, 0x4f, 0xa1, 0x46, 0x13, 0x0b, 0x93, 0x58, 0x78, 0xe3, 0x3a, 0x09, 0x53, 0x79, 0xca, 0x44, - 0x28, 0x2d, 0x4d, 0x89, 0x22, 0xde, 0x0e, 0xf4, 0xd0, 0x9a, 0xcd, 0x3d, 0x1f, 0xf5, 0xec, 0x57, - 0x23, 0xdf, 0xd0, 0x28, 0x02, 0xe1, 0x46, 0x1c, 0x9f, 0x90, 0xe9, 0xde, 0x74, 0x1a, 0x58, 0x61, - 0xe3, 0x06, 0xad, 0x9b, 0x7a, 0x74, 0x50, 0x36, 0x20, 0x28, 0x6d, 0x84, 0x81, 0x6e, 0x9e, 0xbb, - 0xc6, 0xcc, 0x9e, 0x34, 0x6e, 0x72, 0x75, 0xde, 0x0e, 0xda, 0x1c, 0x20, 0x6b, 0xd4, 0x3b, 0x29, - 0x8d, 0xfa, 0x02, 0xe4, 0xcd, 0x31, 0x2e, 0xc7, 0xd7, 0x28, 0xdb, 0x9c, 0x39, 0xee, 0x9a, 0xec, - 0x1d, 0x28, 0xcf, 0x23, 0x11, 0xd8, 0x50, 0x65, 0xbf, 0x41, 0x2c, 0x19, 0xb5, 0x84, 0x02, 0x4d, - 0xd9, 0x47, 0x96, 0x11, 0x2e, 0x7c, 0xeb, 0x91, 0x63, 0x1c, 0x35, 0x6e, 0x51, 0x4e, 0x32, 0x08, - 0x6b, 0xe7, 0x78, 0x47, 0xf6, 0xc4, 0xa0, 0x95, 0x7f, 0x9b, 0xeb, 0x5d, 0x02, 0xd2, 0x35, 0x13, - 0x45, 0xd4, 0x10, 0xca, 0xd4, 0xeb, 0xb2, 0x22, 0x6a, 0x90, 0x36, 0x75, 0xed, 0x31, 0x69, 0xe8, - 0xd4, 0x73, 0x1f, 0x2e, 0x09, 0xa8, 0xd4, 0xf2, 0x92, 0x24, 0xd9, 0xde, 0x86, 0x2c, 0xa7, 0x76, - 0xf3, 0x24, 0xc9, 0xaf, 0xfd, 0x10, 0xd8, 0xea, 0x98, 0xbf, 0x4c, 0x5a, 0xe6, 0x85, 0xb4, 0xfc, - 0xde, 0xe6, 0x27, 0x19, 0xf5, 0x09, 0xd4, 0x52, 0xc2, 0x60, 0xad, 0xd4, 0xe7, 0x2a, 0x97, 0x31, - 0x13, 0xd6, 0x34, 0x4f, 0x44, 0xea, 0xb4, 0xed, 0x1e, 0x09, 0x47, 0x1e, 0x57, 0xa7, 0x29, 0xad, - 0xfe, 0x69, 0x16, 0xaa, 0x7b, 0x46, 0x70, 0xbc, 0x6f, 0xcc, 0x87, 0xa1, 0x11, 0x06, 0x38, 0x45, - 0x8e, 0x8d, 0xe0, 0x78, 0x66, 0xcc, 0xb9, 0xf2, 0x9b, 0xe1, 0x5e, 0x02, 0x01, 0x43, 0xcd, 0x17, - 0x27, 0x27, 0x26, 0x07, 0xee, 0xc1, 0xe7, 0xc2, 0x05, 0x10, 0xa7, 0x51, 0x34, 0x05, 0xc7, 0x8b, - 0xe9, 0x34, 0x2e, 0x2a, 0x4a, 0xb2, 0xdb, 0x50, 0x13, 0x3f, 0x49, 0xf3, 0x3d, 0x13, 0xa7, 0xac, - 0x69, 0x20, 0x7b, 0x08, 0x15, 0x01, 0x18, 0x45, 0x82, 0xb4, 0x1e, 0xbb, 0x76, 0x12, 0x84, 0x26, - 0x53, 0xb1, 0x1f, 0xc1, 0x25, 0x29, 0xf9, 0xc8, 0xf3, 0xf7, 0x17, 0x4e, 0x68, 0xb7, 0xfa, 0x42, - 0xcd, 0x78, 0x65, 0x85, 0x3d, 0x21, 0xd1, 0xd6, 0x73, 0xa6, 0x6b, 0xbb, 0x6f, 0xbb, 0x24, 0x97, - 0xb3, 0x5a, 0x1a, 0xb8, 0x44, 0x65, 0x9c, 0x91, 0x38, 0x4e, 0x53, 0x19, 0x67, 0xb8, 0x60, 0x05, - 0x60, 0xdf, 0x0a, 0x8f, 0x3d, 0x93, 0x74, 0xcc, 0x78, 0xc1, 0x0e, 0x65, 0x94, 0x96, 0xa6, 0xc4, - 0xee, 0x44, 0xfb, 0x6d, 0xe2, 0x86, 0xa4, 0x69, 0x66, 0xb5, 0x28, 0x89, 0x5b, 0x95, 0x6f, 0xb8, - 0x47, 0x56, 0xd0, 0xa8, 0xec, 0x64, 0xef, 0x66, 0x34, 0x91, 0x52, 0xff, 0xe6, 0x26, 0xe4, 0xf9, - 0x48, 0xbe, 0x02, 0xe5, 0x31, 0xf9, 0xc6, 0xd1, 0x10, 0x17, 0xfe, 0x6e, 0x02, 0xf4, 0x17, 0x33, - 0xae, 0x21, 0x0a, 0x17, 0x4e, 0x46, 0xa3, 0xdf, 0x98, 0xa5, 0xb7, 0x08, 0xb1, 0xac, 0x2c, 0x41, - 0x45, 0x0a, 0x2b, 0xe1, 0x7b, 0xa7, 0x34, 0x1b, 0x72, 0x84, 0x88, 0x92, 0xe4, 0x52, 0xa7, 0x5d, - 0x0f, 0x99, 0xf2, 0x84, 0x2b, 0x11, 0xa0, 0xe5, 0x86, 0xcb, 0xee, 0xa6, 0xc2, 0x8a, 0xbb, 0x89, - 0xdd, 0x00, 0xd4, 0x3f, 0x27, 0xd6, 0xc0, 0xb5, 0x5a, 0x7d, 0xea, 0xe1, 0x92, 0x26, 0x41, 0x70, - 0x81, 0x98, 0xde, 0x9c, 0x3a, 0x35, 0xaf, 0xe1, 0x4f, 0xf6, 0x51, 0x3c, 0x3b, 0xa9, 0x8d, 0x42, - 0x5b, 0x17, 0xbb, 0x82, 0x3c, 0x8f, 0xb5, 0x14, 0x1d, 0xe6, 0x84, 0xa2, 0x9e, 0x6b, 0xeb, 0xf8, - 0x53, 0xed, 0x00, 0x68, 0xde, 0x69, 0x60, 0x85, 0xe4, 0x57, 0xbd, 0x42, 0x4d, 0x4c, 0x9d, 0x88, - 0x79, 0xa7, 0x07, 0x5e, 0x10, 0x1b, 0xb4, 0x9b, 0xeb, 0x0d, 0x5a, 0xf5, 0x5d, 0x28, 0xa2, 0x1e, - 0x60, 0x84, 0x06, 0xbb, 0x2d, 0x5c, 0x59, 0x5c, 0x7b, 0x11, 0x3e, 0xbd, 0xa4, 0x0c, 0xe1, 0xdc, - 0xea, 0x45, 0xe5, 0x12, 0xcf, 0x6b, 0x92, 0xc9, 0x18, 0xef, 0x41, 0x22, 0x43, 0xa1, 0x59, 0xbc, - 0x02, 0x65, 0xac, 0x1a, 0x1d, 0x13, 0x08, 0xb9, 0x50, 0xf2, 0xbd, 0xd3, 0x16, 0xa6, 0xd5, 0x7f, - 0x9f, 0x81, 0xca, 0xc0, 0x37, 0x71, 0xf3, 0x1b, 0xce, 0xad, 0xc9, 0x4b, 0xed, 0x6f, 0xd4, 0x43, - 0x3c, 0xc7, 0x31, 0x48, 0xcc, 0x0a, 0x93, 0x2d, 0x06, 0xb0, 0xf7, 0x21, 0x37, 0x45, 0x71, 0x9a, - 0x95, 0xb5, 0x73, 0x29, 0xfb, 0xe8, 0x37, 0x0a, 0x58, 0x8d, 0x48, 0xd5, 0x5f, 0x8f, 0xcb, 0x27, - 0xa9, 0x2b, 0x3b, 0xd3, 0x37, 0xe8, 0x58, 0x6b, 0xd8, 0x52, 0x32, 0xac, 0x04, 0xb9, 0x76, 0x67, - 0xd8, 0xe2, 0x3a, 0x39, 0x6a, 0xe7, 0x43, 0xfd, 0x51, 0x57, 0x1b, 0x8e, 0x94, 0x1c, 0x9d, 0x93, - 0x11, 0xa0, 0xd7, 0x1c, 0x8e, 0x94, 0x12, 0x03, 0x28, 0x1c, 0xf6, 0xbb, 0x3f, 0x3a, 0xec, 0x28, - 0x8a, 0xfa, 0xaf, 0x32, 0x00, 0x89, 0xcf, 0x97, 0xbd, 0x05, 0x95, 0x53, 0x4a, 0xe9, 0xd2, 0x61, - 0x80, 0xdc, 0x46, 0xe0, 0x68, 0xd2, 0x91, 0xde, 0x81, 0x6a, 0xbc, 0x5d, 0xa0, 0xfe, 0xb0, 0x7a, - 0x2a, 0x50, 0x89, 0xf1, 0xbb, 0xe7, 0xec, 0x6d, 0x28, 0x79, 0xd8, 0x0e, 0x24, 0xcd, 0xca, 0xca, - 0x83, 0xd4, 0x7c, 0xad, 0xe8, 0xf1, 0x04, 0xea, 0x19, 0x53, 0x3f, 0x32, 0xc1, 0x63, 0xd2, 0x47, - 0x08, 0x6a, 0x39, 0xc6, 0x22, 0xb0, 0x34, 0x8e, 0x8f, 0xa5, 0x74, 0x3e, 0x91, 0xd2, 0xea, 0x8f, - 0xa1, 0x3e, 0x34, 0x66, 0x73, 0x2e, 0xcb, 0xa9, 0x61, 0x0c, 0x72, 0x38, 0x27, 0xc4, 0xd4, 0xa3, - 0xdf, 0xb8, 0xe8, 0x0e, 0x2c, 0x7f, 0x62, 0xb9, 0xd1, 0x1a, 0x8d, 0x92, 0x28, 0x7e, 0x0f, 0x51, - 0x9a, 0x6b, 0xde, 0x69, 0x24, 0xce, 0xa3, 0xb4, 0xfa, 0x77, 0x32, 0x50, 0x91, 0xaa, 0xc1, 0xde, - 0x85, 0x1c, 0x29, 0xa4, 0x19, 0x59, 0x10, 0x4a, 0x04, 0xfc, 0x37, 0x57, 0x61, 0x90, 0x90, 0xdd, - 0x81, 0x7c, 0x10, 0x1a, 0x7e, 0x74, 0x7c, 0xa0, 0x48, 0x1c, 0xbb, 0xde, 0xc2, 0x35, 0x35, 0x8e, - 0x66, 0x2a, 0x64, 0x2d, 0xd7, 0x14, 0x4e, 0x8b, 0x55, 0x2a, 0x44, 0xaa, 0x3b, 0x50, 0x8e, 0xb3, - 0xc7, 0x29, 0xa0, 0x0d, 0x9e, 0x0e, 0x95, 0x0d, 0x56, 0x86, 0xbc, 0xd6, 0xec, 0x3f, 0xee, 0x28, - 0x19, 0xf5, 0x67, 0x19, 0x80, 0x84, 0x8b, 0xdd, 0x4f, 0xd5, 0xf6, 0xda, 0x72, 0xae, 0xf7, 0xe9, - 0xaf, 0x54, 0xd9, 0xeb, 0x50, 0x5e, 0xb8, 0x04, 0xb4, 0x4c, 0xb1, 0x13, 0x25, 0x00, 0xb4, 0xa2, - 0xa2, 0x60, 0x97, 0x25, 0x2b, 0xea, 0x99, 0xe1, 0xa8, 0xdf, 0x83, 0x72, 0x9c, 0x1d, 0x1a, 0x86, - 0x8f, 0x06, 0xbd, 0xde, 0xe0, 0x69, 0xb7, 0xff, 0x58, 0xd9, 0xc0, 0xe4, 0x81, 0xd6, 0x69, 0x75, - 0xda, 0x98, 0xcc, 0xe0, 0x9c, 0x6d, 0x1d, 0x6a, 0x5a, 0xa7, 0x3f, 0xd2, 0xb5, 0xc1, 0x53, 0x65, - 0x53, 0xfd, 0xed, 0x0c, 0xd4, 0x06, 0x8e, 0xd9, 0xf2, 0x9c, 0x96, 0x31, 0x47, 0x8d, 0x83, 0xfd, - 0x00, 0xb6, 0xc7, 0x0b, 0xd4, 0x7a, 0xe7, 0x8e, 0x31, 0xb1, 0x8e, 0x3d, 0xc7, 0xb4, 0xa2, 0x45, - 0x98, 0x3a, 0x20, 0x11, 0xbe, 0x5c, 0x85, 0x88, 0x0f, 0x12, 0x5a, 0xf6, 0x21, 0x54, 0xe7, 0xbe, - 0x37, 0xb6, 0xf4, 0xc0, 0x5b, 0xf8, 0x13, 0x6b, 0xc5, 0x5c, 0x4b, 0x78, 0x2b, 0x44, 0x37, 0x24, - 0x32, 0xf5, 0xef, 0x6d, 0x42, 0xb5, 0x6d, 0x99, 0x8b, 0xf9, 0x67, 0x9e, 0xed, 0xb6, 0xc2, 0x33, - 0xf6, 0x01, 0x54, 0x3d, 0x87, 0xfc, 0x90, 0xba, 0x74, 0x1c, 0xbf, 0x2e, 0x1f, 0xf0, 0xa8, 0x05, - 0x74, 0x78, 0xff, 0x0e, 0x5c, 0xe0, 0x86, 0xb9, 0xf0, 0x53, 0x9d, 0x71, 0x66, 0x5c, 0x33, 0x79, - 0x4d, 0xe1, 0x28, 0xbe, 0x41, 0x13, 0xf9, 0xaf, 0xc0, 0x45, 0x89, 0x1c, 0x05, 0x0b, 0xa7, 0xcf, - 0xae, 0xac, 0xb1, 0xed, 0x98, 0x37, 0x0e, 0x14, 0xf8, 0x0c, 0x2e, 0x46, 0x35, 0x9c, 0xf0, 0xde, - 0xe3, 0xcc, 0x39, 0xd9, 0xbc, 0x48, 0xf5, 0xae, 0xa8, 0xf0, 0xb6, 0x27, 0x03, 0x29, 0xaf, 0xf7, - 0xe1, 0x92, 0x89, 0xad, 0xd7, 0x79, 0xe7, 0x9f, 0x58, 0xd6, 0x5c, 0x77, 0x8c, 0x20, 0x14, 0xe7, - 0x8e, 0x8c, 0x90, 0xbb, 0x88, 0xfb, 0xdc, 0xb2, 0xe6, 0x3d, 0x23, 0x08, 0xd5, 0xbf, 0x96, 0x85, - 0x32, 0xf7, 0x2a, 0x60, 0x77, 0xdd, 0x85, 0xa2, 0x37, 0xfe, 0x4a, 0xf7, 0x63, 0x6b, 0x7b, 0xe5, - 0x6c, 0xb1, 0xe0, 0x8d, 0xbf, 0xd2, 0xac, 0x29, 0x7b, 0x2b, 0xda, 0xea, 0xd0, 0x32, 0xdf, 0x94, - 0x7d, 0xe3, 0x91, 0x5a, 0x2f, 0xb6, 0x3e, 0x34, 0x39, 0x1f, 0x42, 0xc5, 0x76, 0x03, 0xcb, 0x0f, - 0xb9, 0x27, 0xa5, 0xf8, 0xfc, 0x41, 0xe0, 0x64, 0xe4, 0x5c, 0x79, 0x08, 0x15, 0xee, 0x59, 0xe1, - 0x4c, 0xa5, 0xe7, 0x33, 0x71, 0x32, 0x62, 0xfa, 0x14, 0xea, 0x89, 0x98, 0x23, 0xbe, 0xf2, 0x73, - 0xf9, 0x6a, 0x31, 0x25, 0xb1, 0x3e, 0x80, 0xcb, 0xc1, 0x89, 0x3d, 0xd7, 0x45, 0x4d, 0x3d, 0x97, - 0x8e, 0x15, 0xf4, 0xf9, 0x89, 0x70, 0xe7, 0x33, 0xc4, 0x76, 0x09, 0x39, 0x70, 0xfb, 0x0b, 0xc7, - 0x39, 0x38, 0x61, 0x6f, 0xc2, 0xb6, 0x20, 0x9f, 0x9f, 0x44, 0x73, 0x85, 0x8c, 0xcd, 0xbc, 0x56, - 0xe7, 0x88, 0x83, 0x13, 0xa1, 0xc9, 0x7d, 0x0c, 0x0d, 0x7e, 0xdc, 0x2d, 0x1a, 0x65, 0x4c, 0xa7, - 0xd6, 0x24, 0xd4, 0x51, 0x75, 0x10, 0xbe, 0xff, 0x4b, 0x84, 0xe7, 0xee, 0xa1, 0x26, 0x61, 0x51, - 0xf2, 0xa9, 0x7f, 0x3f, 0x03, 0x65, 0x5e, 0x2c, 0x8e, 0xd0, 0x6b, 0x90, 0x7d, 0xc1, 0xe8, 0x20, - 0x8e, 0xdd, 0x83, 0x6d, 0xc3, 0x34, 0x45, 0x01, 0x96, 0xc9, 0x8b, 0xe0, 0xe2, 0x60, 0xcb, 0x30, - 0xcd, 0xa6, 0x80, 0x93, 0x58, 0xbd, 0x0b, 0x8a, 0x1d, 0xe8, 0x91, 0x5d, 0x99, 0x1c, 0x6e, 0x97, - 0xb4, 0xba, 0x1d, 0x08, 0xb3, 0x92, 0xbb, 0x8a, 0x53, 0x03, 0x9e, 0x7b, 0xf1, 0x80, 0xab, 0x0c, - 0x40, 0xb3, 0x68, 0xed, 0xb7, 0xc2, 0xb3, 0xcf, 0x72, 0xa5, 0x8c, 0x02, 0xea, 0x3f, 0xad, 0x40, - 0xa5, 0xe9, 0x1a, 0xce, 0xf9, 0x4f, 0x2c, 0x3a, 0x99, 0x27, 0xc7, 0xe1, 0x7c, 0x21, 0xba, 0x80, - 0x1f, 0x5b, 0x95, 0x09, 0x42, 0x35, 0xbb, 0x09, 0x15, 0x6f, 0x11, 0xc6, 0x78, 0x7e, 0x90, 0x05, - 0x1c, 0x44, 0x04, 0x31, 0x7f, 0xec, 0x94, 0x8e, 0xf8, 0x49, 0x2b, 0x4f, 0xf8, 0x63, 0x4d, 0x2d, - 0xe6, 0x27, 0x82, 0x5b, 0x50, 0x0b, 0xed, 0x99, 0x45, 0x67, 0x77, 0x8b, 0x99, 0x65, 0xf2, 0x90, - 0x3e, 0x1e, 0x25, 0xd6, 0x12, 0x30, 0xcc, 0x65, 0x66, 0xcd, 0x3c, 0xff, 0x9c, 0xe7, 0x52, 0xe0, - 0xb9, 0x70, 0x10, 0xe5, 0xf2, 0x36, 0xb0, 0x53, 0xc3, 0x0e, 0xf5, 0x74, 0x56, 0x5c, 0x3b, 0x56, - 0x10, 0x33, 0x92, 0xb3, 0xbb, 0x0c, 0x05, 0xd3, 0x0e, 0x4e, 0xba, 0x03, 0xa1, 0x19, 0x8b, 0x14, - 0xb6, 0x25, 0x98, 0x18, 0xb8, 0x31, 0x87, 0x16, 0xd7, 0xe2, 0xb2, 0x5a, 0x19, 0x21, 0xbb, 0x08, - 0x40, 0xc1, 0xee, 0x5a, 0xe1, 0xa9, 0xe7, 0x23, 0x27, 0x57, 0x7c, 0x13, 0x00, 0x6e, 0x80, 0x48, - 0x8a, 0x05, 0xd1, 0xdc, 0xcb, 0x6a, 0x71, 0x1a, 0x55, 0x4a, 0x3e, 0x0f, 0x09, 0x5b, 0xe5, 0xd5, - 0x4f, 0x20, 0xec, 0x36, 0xd4, 0xa9, 0xfa, 0xa4, 0x18, 0x63, 0x1b, 0xe8, 0xac, 0x29, 0xab, 0x55, - 0x11, 0x4a, 0x76, 0x32, 0x52, 0x7d, 0x0a, 0x57, 0x53, 0xed, 0xd3, 0x0d, 0xdf, 0x37, 0xce, 0xf5, - 0x99, 0xf1, 0x95, 0xe7, 0x93, 0x4f, 0x23, 0xab, 0x5d, 0x96, 0xbb, 0xad, 0x89, 0xe8, 0x7d, 0xc4, - 0x3e, 0x97, 0xd5, 0x76, 0x3d, 0x9f, 0x1c, 0x1e, 0x6b, 0x59, 0x11, 0x4b, 0xd6, 0x39, 0x0d, 0x30, - 0x69, 0xe9, 0x01, 0x8f, 0x2e, 0xd4, 0x2a, 0x04, 0xdb, 0x25, 0x10, 0xea, 0xa9, 0xc1, 0x43, 0x2e, - 0x2f, 0xb7, 0x45, 0xa8, 0xcf, 0x43, 0x92, 0x84, 0x1c, 0x71, 0x6c, 0x19, 0x26, 0x9d, 0x5f, 0x11, - 0x62, 0xcf, 0x32, 0xe8, 0x74, 0x38, 0x78, 0xa8, 0xcf, 0x17, 0x21, 0x0f, 0x0b, 0xd4, 0xf2, 0xc1, - 0xc3, 0x83, 0x45, 0x28, 0xc0, 0x47, 0x56, 0x48, 0xc7, 0x55, 0x04, 0x7e, 0x6c, 0x85, 0xa8, 0x6c, - 0x06, 0x0f, 0x23, 0x07, 0xef, 0x25, 0xd1, 0xb7, 0x0f, 0x85, 0x07, 0x57, 0x85, 0x5a, 0x8c, 0xd4, - 0x67, 0x0b, 0x1e, 0x07, 0x98, 0xd5, 0x2a, 0x11, 0xc1, 0xfe, 0xc2, 0xc1, 0x81, 0x9d, 0x18, 0x93, - 0x63, 0x4b, 0xf7, 0xb1, 0x2a, 0x57, 0xf8, 0xd0, 0x11, 0x44, 0xc3, 0xda, 0xbc, 0x02, 0x3c, 0xa1, - 0x1f, 0xdb, 0x21, 0x39, 0x5e, 0xb2, 0x5a, 0x89, 0x00, 0x7b, 0x76, 0x88, 0xeb, 0x98, 0x23, 0xc5, - 0x0c, 0xa4, 0x2c, 0xae, 0x12, 0xd1, 0x16, 0x21, 0xf6, 0x09, 0x4e, 0x19, 0xdd, 0x05, 0x25, 0x45, - 0x8b, 0xf9, 0x5d, 0x23, 0xd2, 0xba, 0x44, 0x8a, 0xb9, 0xde, 0x01, 0xce, 0xac, 0xe3, 0xd4, 0xe3, - 0x79, 0xbe, 0xc2, 0xad, 0x34, 0x02, 0xb7, 0xed, 0xe0, 0x84, 0x72, 0xbc, 0x0d, 0x75, 0x89, 0x0e, - 0xf3, 0xbb, 0xce, 0x67, 0x46, 0x4c, 0x96, 0xaa, 0xa3, 0x6f, 0xcd, 0xbc, 0x50, 0x34, 0xf3, 0x55, - 0xa9, 0x8e, 0x1a, 0xc1, 0xd3, 0x75, 0x14, 0xb4, 0x98, 0xe7, 0x0d, 0xa9, 0x8e, 0x9c, 0x14, 0x73, - 0x7d, 0x0d, 0xaa, 0xa7, 0xbe, 0x1d, 0x86, 0x96, 0xcb, 0x17, 0xff, 0x4d, 0xde, 0xb1, 0x02, 0x46, - 0xab, 0xff, 0x35, 0xa8, 0xf2, 0x9e, 0x17, 0xf2, 0x6d, 0x87, 0x93, 0x08, 0x58, 0x24, 0x20, 0x44, - 0x6f, 0xcc, 0x6c, 0x97, 0xbc, 0x2b, 0x59, 0xad, 0xcc, 0x21, 0x68, 0xac, 0x4a, 0x68, 0xe3, 0x8c, - 0x7c, 0x2c, 0x09, 0xda, 0x38, 0xa3, 0x25, 0x39, 0xb7, 0x1d, 0x87, 0x2f, 0xfc, 0x5b, 0x62, 0x49, - 0x22, 0x84, 0xd6, 0x7d, 0x8c, 0xa6, 0xd2, 0x6f, 0x4b, 0x68, 0x2a, 0x1b, 0x27, 0x0e, 0xa1, 0xb1, - 0xe8, 0xd7, 0xc5, 0xc4, 0x41, 0x00, 0x96, 0x9c, 0x20, 0x8d, 0xb3, 0xc6, 0x1d, 0x19, 0x69, 0x9c, - 0x09, 0xb9, 0x85, 0xb9, 0x12, 0xef, 0x1b, 0xb1, 0xdc, 0x42, 0x10, 0x72, 0xcb, 0x04, 0xc6, 0x59, - 0xe3, 0x6e, 0x9a, 0xc0, 0x38, 0x23, 0x0b, 0xc9, 0x32, 0x4c, 0x5e, 0xf1, 0x37, 0x79, 0xf6, 0x08, - 0xa0, 0x7a, 0xef, 0x40, 0x35, 0x78, 0xa8, 0x27, 0xf8, 0x7b, 0x9c, 0x3d, 0x78, 0xa8, 0x45, 0x14, - 0xb7, 0xa1, 0x1e, 0x4f, 0x0d, 0x4e, 0xf3, 0x16, 0x1f, 0x78, 0x53, 0x4c, 0x0d, 0x3a, 0xee, 0xfb, - 0x69, 0x06, 0xae, 0x0d, 0xc8, 0x39, 0x44, 0xe2, 0x7f, 0xdf, 0x0a, 0x02, 0xe3, 0xc8, 0x7a, 0xe4, - 0xf9, 0x8f, 0x16, 0x3f, 0xf9, 0xc9, 0x39, 0xbb, 0x0b, 0x5b, 0x07, 0x86, 0x6f, 0xb9, 0x61, 0x7c, - 0x50, 0x25, 0x3c, 0x33, 0xcb, 0x60, 0xf6, 0x09, 0x28, 0x1c, 0xc4, 0x43, 0xa2, 0x5a, 0xd1, 0x39, - 0xcd, 0xb2, 0x5f, 0x79, 0x85, 0x0a, 0x8d, 0xbd, 0x72, 0xdb, 0x0e, 0x42, 0x0d, 0x2d, 0x7d, 0xf6, - 0x29, 0x28, 0x8e, 0x77, 0x8a, 0x16, 0x0b, 0xaa, 0xb1, 0xba, 0xa4, 0x38, 0x8b, 0x5d, 0x32, 0xd1, - 0x96, 0xeb, 0x44, 0x98, 0xa8, 0xbb, 0x9f, 0x82, 0xb2, 0x98, 0xcf, 0xd3, 0xac, 0x9b, 0xcf, 0x61, - 0x25, 0xc2, 0x84, 0xf5, 0x2d, 0xa8, 0x48, 0xa5, 0xae, 0x51, 0xae, 0x21, 0x29, 0x0b, 0x89, 0xa5, - 0x72, 0xd6, 0x84, 0x59, 0x42, 0x92, 0xbb, 0xfa, 0xff, 0x6d, 0x82, 0x42, 0xce, 0x31, 0x8d, 0x0e, - 0xeb, 0xe9, 0xbc, 0x2b, 0x65, 0x96, 0x65, 0x5e, 0x6a, 0x96, 0xed, 0x40, 0xde, 0xb1, 0x67, 0x71, - 0xec, 0x53, 0xea, 0xc0, 0x86, 0x10, 0x38, 0xd6, 0x9e, 0x6f, 0x1f, 0x91, 0x01, 0x29, 0x47, 0xe9, - 0x91, 0xdf, 0x0f, 0xed, 0x31, 0x1a, 0xa2, 0xfb, 0x00, 0xa6, 0x1d, 0x84, 0x3a, 0xb9, 0x54, 0x44, - 0xb5, 0x45, 0xcf, 0xc4, 0xfd, 0xaf, 0x95, 0xcd, 0x78, 0x28, 0xee, 0x82, 0x22, 0x29, 0x61, 0x6e, - 0xec, 0x0d, 0xc9, 0x6b, 0x89, 0x72, 0xd6, 0x72, 0x5b, 0x6e, 0xb8, 0x42, 0x89, 0xea, 0x53, 0x61, - 0x85, 0xb2, 0x6b, 0x9e, 0xa9, 0x7f, 0xf6, 0x1a, 0xe4, 0xfa, 0x9e, 0x69, 0xb1, 0xf7, 0xa0, 0x4c, - 0xe1, 0xac, 0xd2, 0x00, 0x0b, 0x25, 0x19, 0xd1, 0xf4, 0x87, 0x46, 0xaa, 0xe4, 0x8a, 0x5f, 0xcf, - 0x0f, 0x80, 0x7d, 0x8d, 0x8c, 0x3b, 0x3a, 0x92, 0xc5, 0x26, 0x55, 0x84, 0x03, 0x8a, 0xfc, 0x25, - 0x1c, 0x83, 0x7b, 0x2b, 0xb9, 0xef, 0x7d, 0xcb, 0x25, 0x8d, 0x3c, 0xaf, 0xc5, 0x69, 0x32, 0xa9, - 0x7d, 0x0f, 0x55, 0x2f, 0xbe, 0x03, 0xe5, 0xd7, 0x98, 0xd4, 0x1c, 0x4f, 0x5b, 0xd2, 0x7b, 0x50, - 0xfe, 0xca, 0xb3, 0x5d, 0x5e, 0xf1, 0xc2, 0x4a, 0xc5, 0xd1, 0x62, 0xe1, 0x15, 0xff, 0x4a, 0xfc, - 0x62, 0xb7, 0xa0, 0xe8, 0xb9, 0x3c, 0xef, 0xe2, 0x4a, 0xde, 0x05, 0xcf, 0xed, 0xf1, 0xe8, 0xaa, - 0x9a, 0x1d, 0xe8, 0xbe, 0x7d, 0x74, 0x1c, 0xea, 0xc8, 0x29, 0x8e, 0x72, 0x2b, 0x76, 0xa0, 0x21, - 0x0c, 0xb3, 0xc5, 0x89, 0x37, 0xb5, 0x1d, 0xd4, 0xf0, 0x28, 0xb3, 0xf2, 0x4a, 0x66, 0xc0, 0xd1, - 0x94, 0xe1, 0xeb, 0x50, 0x3a, 0xf2, 0x3d, 0x34, 0x22, 0xce, 0x1b, 0xb0, 0x42, 0x59, 0x24, 0xdc, - 0xee, 0x39, 0xaa, 0x4f, 0xf4, 0xd3, 0x76, 0x8f, 0x74, 0xf2, 0x92, 0x54, 0x76, 0xb2, 0x77, 0x4b, - 0x5a, 0x35, 0x02, 0x92, 0xff, 0xe3, 0x75, 0x28, 0x19, 0x47, 0x47, 0xba, 0x08, 0x12, 0x5b, 0xc9, - 0xcb, 0x38, 0x3a, 0xa2, 0x22, 0xef, 0x43, 0xed, 0xd4, 0x76, 0xf5, 0x60, 0x6e, 0x4d, 0x38, 0x6d, - 0x6d, 0xb5, 0x2b, 0x4f, 0x6d, 0x17, 0x67, 0x37, 0xd1, 0xcb, 0xcb, 0xa0, 0xfe, 0xcd, 0x97, 0xc1, - 0xd6, 0xf3, 0x96, 0x81, 0x0a, 0x05, 0xe1, 0xda, 0x57, 0x56, 0x48, 0x04, 0x86, 0xbd, 0x0f, 0x15, - 0xdf, 0x70, 0x4f, 0x74, 0x71, 0x2e, 0xfe, 0xa5, 0x6c, 0xe9, 0x6b, 0x86, 0x7b, 0x22, 0x8e, 0xc5, - 0xc1, 0x8f, 0x7f, 0xa7, 0x55, 0xe6, 0xed, 0x97, 0xd8, 0x48, 0x92, 0xe9, 0xc5, 0x5e, 0x6c, 0x7a, - 0x7d, 0x48, 0x36, 0x8e, 0xe5, 0x86, 0x7a, 0xc4, 0x70, 0x61, 0x3d, 0x43, 0x95, 0x93, 0x0d, 0x38, - 0x1b, 0x36, 0x80, 0x3c, 0x6d, 0x3a, 0xb9, 0xe5, 0x2e, 0xa6, 0x1a, 0x10, 0xbb, 0xe0, 0x34, 0xf0, - 0x13, 0x77, 0x5c, 0x13, 0xb6, 0x92, 0xc8, 0x59, 0x1e, 0x82, 0x7c, 0x49, 0x76, 0xf5, 0xa7, 0x42, - 0x6d, 0x23, 0xab, 0xca, 0x4e, 0xc5, 0xdf, 0xde, 0x82, 0x1a, 0x0f, 0x44, 0xe1, 0xfd, 0x16, 0x90, - 0x92, 0x54, 0xd6, 0xaa, 0x04, 0xe4, 0xfd, 0x14, 0x90, 0x80, 0x11, 0xa6, 0x5e, 0x78, 0x46, 0x5a, - 0x52, 0x22, 0x60, 0xb8, 0x6d, 0x17, 0x9e, 0x69, 0x65, 0x33, 0xfa, 0x89, 0x9b, 0xff, 0xd8, 0x76, - 0x4d, 0x9c, 0x7a, 0xa1, 0x71, 0x14, 0x34, 0x1a, 0xb4, 0x32, 0x2b, 0x02, 0x36, 0x32, 0x8e, 0x02, - 0x34, 0xfc, 0x0d, 0x6e, 0x6c, 0xf0, 0x7a, 0x5f, 0x95, 0x3d, 0x53, 0x92, 0x19, 0xa2, 0x55, 0x0c, - 0xc9, 0x26, 0xf9, 0x18, 0x58, 0x74, 0xf2, 0x28, 0xd9, 0xf1, 0xd7, 0x56, 0x66, 0xe3, 0x96, 0x38, - 0x7a, 0x8c, 0xad, 0xf8, 0x9b, 0x50, 0xe1, 0x9e, 0x0a, 0x3d, 0x08, 0xad, 0x79, 0xe3, 0x15, 0xaa, - 0x10, 0x70, 0xd0, 0x30, 0xb4, 0xe6, 0xec, 0x63, 0xa8, 0xa5, 0xad, 0xac, 0xeb, 0x6b, 0x0e, 0xf0, - 0x68, 0x5a, 0x68, 0xd5, 0x89, 0x6c, 0x77, 0xdd, 0xe2, 0x51, 0xdd, 0xa4, 0x21, 0x11, 0x23, 0x3f, - 0xa4, 0xaa, 0xba, 0x5e, 0xd8, 0x8a, 0x60, 0xd8, 0x81, 0x91, 0x81, 0x1d, 0x9e, 0x91, 0x52, 0x15, - 0x77, 0x60, 0x6c, 0x3a, 0xa2, 0x71, 0x14, 0x59, 0x91, 0x6d, 0xe0, 0x81, 0x17, 0xb4, 0xc9, 0x5b, - 0x3e, 0x0f, 0xb2, 0x20, 0x1d, 0x2a, 0x3e, 0xd3, 0x5b, 0xde, 0x7b, 0x34, 0x1e, 0x90, 0x22, 0xef, - 0x46, 0x9f, 0x40, 0x7d, 0xee, 0x5b, 0xba, 0x54, 0xb2, 0x2a, 0x37, 0xea, 0xc0, 0xb7, 0x92, 0xc2, - 0xab, 0x73, 0x29, 0xc5, 0x7e, 0x00, 0xdb, 0x12, 0xe7, 0xe2, 0x84, 0x98, 0x6f, 0x11, 0xf3, 0xc5, - 0x25, 0xe6, 0xc3, 0x13, 0x64, 0xaf, 0xcf, 0x53, 0x69, 0x76, 0x11, 0xf2, 0xdd, 0xa0, 0xe3, 0x9a, - 0xa4, 0x5b, 0x95, 0x34, 0x9e, 0x60, 0x0f, 0xa1, 0xca, 0x0d, 0x19, 0x8a, 0x53, 0x0d, 0x1a, 0x77, - 0x64, 0xd7, 0x33, 0x59, 0x33, 0x84, 0xd0, 0x2a, 0x4e, 0xfc, 0x3b, 0x60, 0x1f, 0xc1, 0x36, 0x3f, - 0x17, 0x90, 0x45, 0xe4, 0x1b, 0xab, 0x43, 0x4e, 0x44, 0x8f, 0x12, 0x39, 0xa9, 0xc1, 0x55, 0x7f, - 0xe1, 0x92, 0x71, 0x23, 0x38, 0xb9, 0xc7, 0x8a, 0xf8, 0xef, 0x12, 0xff, 0x15, 0xb1, 0xba, 0x38, - 0x19, 0xe7, 0x25, 0xd9, 0x74, 0xd9, 0x97, 0x41, 0x07, 0xc8, 0xf7, 0x9c, 0x3c, 0xb9, 0x27, 0x87, - 0xf2, 0x7c, 0xf3, 0xdb, 0xe4, 0x49, 0x5e, 0x1e, 0xca, 0x93, 0x41, 0x6e, 0xb1, 0xb0, 0x4d, 0xd2, - 0xf4, 0xaa, 0x1a, 0xfd, 0x66, 0xaf, 0x43, 0xdd, 0xb7, 0x26, 0x0b, 0x3f, 0xb0, 0x9f, 0x59, 0x7a, - 0x60, 0xbb, 0x27, 0xa4, 0xe3, 0x95, 0xb4, 0x5a, 0x0c, 0x1d, 0xda, 0xee, 0x09, 0x8a, 0x0c, 0xeb, - 0x2c, 0xb4, 0x7c, 0x97, 0x87, 0xce, 0xbf, 0x2d, 0x8b, 0x8c, 0x0e, 0x21, 0x70, 0x9d, 0x6b, 0x60, - 0xc5, 0xbf, 0x97, 0x46, 0x36, 0xe0, 0x23, 0x7b, 0xff, 0x1b, 0x8d, 0xec, 0x90, 0x46, 0xf6, 0x0e, - 0x94, 0x6c, 0x37, 0xb4, 0xfc, 0x67, 0x86, 0xd3, 0x78, 0x77, 0x45, 0x1a, 0xc7, 0x38, 0x76, 0x1b, - 0x8a, 0x81, 0x63, 0xe3, 0x7a, 0x6f, 0xbc, 0xb7, 0x42, 0x16, 0xa1, 0xd8, 0x5d, 0x28, 0xc7, 0xb7, - 0xbe, 0x1a, 0xef, 0xaf, 0xd0, 0x25, 0x48, 0x76, 0x03, 0x72, 0xa7, 0x38, 0xa1, 0x1e, 0xac, 0x1e, - 0x15, 0x20, 0x1c, 0xb7, 0xef, 0x29, 0xea, 0xec, 0xb4, 0x7d, 0x3f, 0x5c, 0xd9, 0xbe, 0x1f, 0xd9, - 0x8e, 0xc3, 0xb7, 0xef, 0xa9, 0xf8, 0x85, 0x9b, 0x1f, 0x71, 0x60, 0x4b, 0x3e, 0x58, 0xdd, 0xfc, - 0x10, 0xf7, 0x84, 0xee, 0xc7, 0x55, 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, 0xf1, - 0x82, 0x43, 0xba, 0xe6, 0x19, 0x7b, 0x0f, 0x6a, 0x51, 0xb8, 0x10, 0x16, 0x17, 0x34, 0x3e, 0x5e, - 0xa9, 0x41, 0x9a, 0x80, 0xb5, 0xa1, 0x3a, 0x45, 0xdd, 0x7d, 0xc6, 0x55, 0xf9, 0xc6, 0x27, 0x54, - 0x91, 0x9d, 0x68, 0x63, 0x7d, 0x9e, 0xaa, 0xaf, 0xa5, 0xb8, 0xd8, 0x7d, 0x60, 0xf6, 0x94, 0x8f, - 0xe7, 0x23, 0xdf, 0x9b, 0x71, 0x75, 0xbd, 0xf1, 0x29, 0xf7, 0xa0, 0xad, 0x62, 0xe8, 0x30, 0xd0, - 0x72, 0x4d, 0x7d, 0x16, 0x08, 0x35, 0xe1, 0x7b, 0x54, 0x4f, 0x21, 0xbc, 0xe2, 0xdb, 0xa1, 0x91, - 0xb3, 0x17, 0x69, 0xf7, 0x03, 0xae, 0x35, 0x7c, 0x0a, 0x38, 0x5d, 0x9f, 0x25, 0xac, 0xbf, 0xf2, - 0x42, 0x56, 0xa4, 0x8d, 0x58, 0x3f, 0x81, 0x3a, 0x77, 0x94, 0x92, 0x46, 0x86, 0x53, 0xf4, 0xfb, - 0xb2, 0xe4, 0x92, 0x5d, 0xc8, 0x5a, 0xd5, 0x94, 0x1d, 0xca, 0x1f, 0xc3, 0x56, 0xe4, 0xeb, 0x0d, - 0x85, 0x5b, 0xf8, 0x57, 0xe5, 0x62, 0x63, 0x5f, 0xaa, 0x56, 0x5b, 0x44, 0x3f, 0xa9, 0xc8, 0x87, - 0x50, 0xa3, 0x5d, 0x34, 0x70, 0x8d, 0x79, 0x70, 0xec, 0x85, 0x8d, 0x5f, 0x93, 0x15, 0x82, 0xa1, - 0x80, 0x6a, 0x55, 0x24, 0x8a, 0x52, 0x28, 0xfc, 0x93, 0x75, 0x3a, 0x09, 0xad, 0xc6, 0x0f, 0xb8, - 0xf0, 0x8f, 0x81, 0xad, 0xd0, 0x62, 0x0f, 0x01, 0x8c, 0xf9, 0xdc, 0x39, 0xe7, 0x53, 0xf3, 0x87, - 0x34, 0x35, 0x2f, 0x4a, 0x53, 0xb3, 0x89, 0x48, 0x9a, 0x9b, 0x65, 0x23, 0xfa, 0xc9, 0x1e, 0x40, - 0x75, 0xee, 0x05, 0xa1, 0x6e, 0xce, 0x1c, 0x6a, 0x7f, 0x53, 0x5e, 0xdb, 0x07, 0x5e, 0x10, 0xb6, - 0x67, 0x0e, 0xb6, 0x02, 0xe6, 0xf1, 0x6f, 0xd6, 0x83, 0x0b, 0x9e, 0xab, 0x9b, 0x8b, 0xb9, 0x63, - 0x4f, 0xb0, 0x07, 0x0c, 0x3a, 0x7a, 0x6f, 0xec, 0x52, 0x89, 0xd7, 0xa5, 0x12, 0x07, 0x6e, 0x3b, - 0x22, 0x12, 0xb1, 0x6b, 0xdb, 0xde, 0x32, 0x88, 0xec, 0x4c, 0x1a, 0x83, 0x38, 0x80, 0xb3, 0xc5, - 0x55, 0x03, 0x82, 0x46, 0x11, 0x9c, 0x9f, 0xc0, 0x56, 0x42, 0x85, 0x0d, 0x0c, 0x1a, 0x6d, 0x79, - 0x26, 0x4b, 0x11, 0xe1, 0xb5, 0x88, 0x11, 0x61, 0x01, 0xf5, 0x9d, 0xe7, 0x38, 0x8b, 0xb9, 0x10, - 0xa5, 0x8d, 0x8e, 0xe8, 0x3b, 0x02, 0x72, 0x29, 0x29, 0x99, 0xe2, 0xd6, 0xac, 0xf1, 0x48, 0x36, - 0xc5, 0xad, 0x19, 0xaa, 0x19, 0x22, 0x3a, 0xf7, 0x99, 0x6d, 0x9d, 0x06, 0x8d, 0xc7, 0x14, 0xb9, - 0x29, 0xe2, 0x9f, 0x9f, 0x20, 0x08, 0xf7, 0x7d, 0xd3, 0xf6, 0xd1, 0x04, 0xa0, 0xa0, 0xab, 0x3d, - 0x1e, 0x40, 0xcb, 0x41, 0x48, 0xa1, 0xfe, 0x2c, 0x0f, 0xa5, 0xc8, 0x26, 0x61, 0x15, 0x28, 0x1e, - 0xf6, 0x3f, 0xef, 0x0f, 0x9e, 0xf6, 0xf9, 0xed, 0xb8, 0xe6, 0x70, 0xd8, 0xd1, 0x46, 0x8a, 0xc9, - 0xea, 0x00, 0x74, 0xff, 0x45, 0x1f, 0xb6, 0x9a, 0x7d, 0x7e, 0x5b, 0x8e, 0x6e, 0xdd, 0xf0, 0xf4, - 0x26, 0xdb, 0x86, 0xda, 0xa3, 0xc3, 0x3e, 0x05, 0xea, 0x71, 0x50, 0x16, 0x41, 0x9d, 0x2f, 0xf8, - 0xd1, 0x22, 0x07, 0xe5, 0x10, 0xb4, 0xdf, 0x1c, 0x75, 0xb4, 0x6e, 0x04, 0xca, 0x53, 0xcc, 0xdf, - 0xe0, 0x50, 0x6b, 0x89, 0x9c, 0x0a, 0xec, 0x12, 0x6c, 0xc7, 0x6c, 0x51, 0x96, 0x4a, 0x11, 0x6b, - 0x76, 0xa0, 0x0d, 0x3e, 0xeb, 0xb4, 0x46, 0x0a, 0xd0, 0x39, 0xe5, 0xe3, 0xc7, 0x4a, 0x85, 0x55, - 0xa1, 0xd4, 0xee, 0x0e, 0x47, 0xdd, 0x7e, 0x6b, 0xa4, 0x54, 0xb1, 0xc2, 0x8f, 0xba, 0xbd, 0x51, - 0x47, 0x53, 0x6a, 0xac, 0x04, 0xb9, 0xcf, 0x06, 0xdd, 0xbe, 0x52, 0xa7, 0x7b, 0x40, 0xcd, 0xfd, - 0x83, 0x5e, 0x47, 0xd9, 0x42, 0xe8, 0x70, 0xa0, 0x8d, 0x14, 0x05, 0xa1, 0x4f, 0xbb, 0xfd, 0xf6, - 0xe0, 0xa9, 0xb2, 0xcd, 0xca, 0x90, 0x3f, 0xec, 0x63, 0x31, 0x8c, 0xd5, 0xa0, 0x4c, 0x3f, 0xf5, - 0x66, 0xaf, 0xa7, 0x5c, 0x90, 0x0e, 0x37, 0x2f, 0x22, 0x8a, 0x8e, 0x4a, 0x87, 0x58, 0x87, 0x4b, - 0xd8, 0x96, 0x38, 0x49, 0xd4, 0x97, 0x31, 0x9f, 0xfd, 0x6e, 0xff, 0x70, 0xa8, 0x5c, 0x41, 0x62, - 0xfa, 0x49, 0x98, 0x06, 0xe6, 0xd3, 0xed, 0x53, 0x57, 0xde, 0xc0, 0xdf, 0xed, 0x4e, 0xaf, 0x33, - 0xea, 0x28, 0x37, 0xb1, 0x55, 0xbd, 0x41, 0xeb, 0x73, 0x7d, 0x70, 0xa0, 0xbc, 0x86, 0x7d, 0x7a, - 0xa0, 0x75, 0x74, 0x41, 0x78, 0x8b, 0x29, 0x50, 0x7d, 0x74, 0xf8, 0xe3, 0x1f, 0x7f, 0xa9, 0x8b, - 0x46, 0xbd, 0x8e, 0x65, 0x26, 0x14, 0xfa, 0xe1, 0xe7, 0xca, 0x9d, 0x25, 0xd0, 0xf0, 0x73, 0xe5, - 0x0d, 0xec, 0x94, 0xa8, 0x97, 0x95, 0xbb, 0x48, 0xa0, 0x75, 0x5a, 0x87, 0xda, 0xb0, 0xfb, 0xa4, - 0xa3, 0xb7, 0x46, 0x1d, 0xe5, 0x4d, 0xea, 0x85, 0x6e, 0xff, 0x73, 0xe5, 0x1e, 0x56, 0x13, 0x7f, - 0xf1, 0xbe, 0x7f, 0x8b, 0x31, 0xa8, 0x27, 0xb4, 0x04, 0x7b, 0x1b, 0x49, 0x76, 0xb5, 0x41, 0xb3, - 0xdd, 0x6a, 0x0e, 0x47, 0xca, 0x3b, 0xd8, 0xc6, 0xe1, 0x41, 0xaf, 0x3b, 0x52, 0xee, 0x63, 0x43, - 0x1e, 0x37, 0x47, 0x7b, 0x1d, 0x4d, 0x79, 0x17, 0x87, 0x71, 0xd4, 0xdd, 0xef, 0xe8, 0xa2, 0x4f, - 0x1f, 0x60, 0x19, 0x8f, 0xba, 0xbd, 0x9e, 0xf2, 0x90, 0x0e, 0xe7, 0x9a, 0xda, 0xa8, 0x4b, 0x03, - 0xf9, 0x01, 0x66, 0xd0, 0x3c, 0x38, 0xe8, 0x7d, 0xa9, 0x7c, 0x88, 0x0d, 0xdc, 0x3f, 0xec, 0x8d, - 0xba, 0xfa, 0xe1, 0x41, 0xbb, 0x39, 0xea, 0x28, 0x1f, 0xd1, 0x28, 0x0f, 0x86, 0xa3, 0xf6, 0x7e, - 0x4f, 0xf9, 0x98, 0xf2, 0xa4, 0x39, 0xd6, 0xea, 0x0d, 0xfa, 0x1d, 0xe5, 0x13, 0x35, 0x57, 0xda, - 0x51, 0x76, 0xd4, 0x5c, 0x49, 0x55, 0x54, 0xf5, 0x37, 0xa1, 0x14, 0x19, 0xa4, 0x98, 0x65, 0xb7, - 0xdf, 0xef, 0x68, 0xca, 0x06, 0x16, 0xdb, 0xeb, 0x3c, 0x1a, 0x29, 0x19, 0x3a, 0xb5, 0xec, 0x3e, - 0xde, 0x1b, 0x29, 0x9b, 0xf8, 0x73, 0x70, 0x88, 0x3d, 0x98, 0xa5, 0xa6, 0x77, 0xf6, 0xbb, 0x4a, - 0x0e, 0x7f, 0x35, 0xfb, 0xa3, 0xae, 0x92, 0xa7, 0x09, 0xd2, 0xed, 0x3f, 0xee, 0x75, 0x94, 0x02, - 0x42, 0xf7, 0x9b, 0xda, 0xe7, 0x4a, 0x91, 0x67, 0xda, 0xee, 0x7c, 0xa1, 0x94, 0x58, 0x01, 0x36, - 0x7b, 0x0f, 0x94, 0x32, 0x82, 0xda, 0x9d, 0xf6, 0xe1, 0x81, 0x02, 0xea, 0x5d, 0x28, 0x36, 0x8f, - 0x8e, 0xf6, 0xd1, 0xde, 0xc7, 0x96, 0x1e, 0xf6, 0x7a, 0x7c, 0xc1, 0xec, 0x0e, 0x46, 0xa3, 0xc1, - 0xbe, 0x92, 0xc1, 0x29, 0x3a, 0x1a, 0x1c, 0x28, 0x9b, 0x6a, 0x17, 0x4a, 0xd1, 0xd6, 0x2b, 0x5d, - 0x62, 0x2b, 0x41, 0xee, 0x40, 0xeb, 0x3c, 0xe1, 0x47, 0xed, 0xfd, 0xce, 0x17, 0x58, 0x4d, 0xfc, - 0x85, 0x19, 0x65, 0xb1, 0x20, 0x7e, 0xdb, 0x8c, 0x6e, 0xb1, 0xf5, 0xba, 0xfd, 0x4e, 0x53, 0x53, - 0xf2, 0xea, 0x87, 0xb0, 0xbd, 0x22, 0xb8, 0xa8, 0xf8, 0x66, 0x57, 0x14, 0xdf, 0x7d, 0xdc, 0x1f, - 0x68, 0x1d, 0x7e, 0x2d, 0x4e, 0x74, 0xea, 0xa6, 0xfa, 0x16, 0x94, 0x63, 0x09, 0x8b, 0x93, 0xac, - 0xa5, 0x0d, 0x86, 0x43, 0x3e, 0x06, 0x1b, 0x98, 0xa6, 0xbe, 0xe1, 0xe9, 0xcc, 0x67, 0xb9, 0xd2, - 0x4d, 0x65, 0xe7, 0xb3, 0x5c, 0xe9, 0xb6, 0xf2, 0xba, 0x3a, 0x84, 0xed, 0x48, 0xd0, 0x53, 0x48, - 0x3e, 0x59, 0x20, 0x17, 0x21, 0xdf, 0xb3, 0x9e, 0x59, 0x4e, 0x14, 0x5b, 0x4e, 0x09, 0x84, 0x0e, - 0xc6, 0x5f, 0x75, 0xe3, 0x5b, 0xcc, 0x94, 0x40, 0xcd, 0xae, 0x2f, 0x5d, 0xa4, 0xa6, 0x7b, 0x82, - 0x7f, 0x25, 0x03, 0xa5, 0x78, 0xfb, 0xb8, 0x0d, 0x9b, 0xa3, 0xa1, 0x38, 0x2b, 0xba, 0x78, 0x3f, - 0x79, 0x37, 0x62, 0x14, 0xfd, 0xd2, 0x36, 0x47, 0x43, 0xf6, 0x36, 0x14, 0xf8, 0xbd, 0x4f, 0xe1, - 0x27, 0xba, 0x98, 0xde, 0x92, 0x46, 0x84, 0xd3, 0x04, 0x0d, 0xfb, 0x10, 0xca, 0x71, 0x6d, 0x85, - 0xe3, 0xe4, 0x4a, 0x9a, 0x21, 0x46, 0x6b, 0x09, 0xa5, 0xda, 0x83, 0x7a, 0x3a, 0x43, 0x76, 0x03, - 0x80, 0x67, 0x29, 0x79, 0x07, 0x25, 0x08, 0xbb, 0x06, 0xd1, 0x75, 0xd4, 0x36, 0x55, 0xac, 0x16, - 0x5f, 0x4f, 0x6d, 0xab, 0xff, 0x32, 0x0b, 0x90, 0x28, 0xa0, 0xd8, 0x11, 0xb1, 0x3b, 0x28, 0x2f, - 0x0e, 0xc3, 0x5f, 0x81, 0xb2, 0xe3, 0x19, 0xa6, 0xfc, 0x6c, 0x44, 0x09, 0x01, 0x34, 0x4c, 0xf2, - 0xd5, 0xac, 0x32, 0x8f, 0x44, 0x61, 0x97, 0xa1, 0x30, 0xf5, 0xfc, 0x99, 0x11, 0x8a, 0x8b, 0x04, - 0x22, 0x85, 0xfb, 0x88, 0x7d, 0xe4, 0x7a, 0xbe, 0x85, 0x6a, 0xb8, 0x4b, 0x77, 0x09, 0x70, 0x0c, - 0xaa, 0x02, 0xd8, 0x43, 0x18, 0xee, 0x03, 0x96, 0x3b, 0x71, 0xbc, 0xc0, 0x32, 0xf5, 0x31, 0x0f, - 0xee, 0xa9, 0x6a, 0x10, 0x81, 0x76, 0xcf, 0x79, 0x6b, 0xfd, 0x99, 0xed, 0x1a, 0xa1, 0x38, 0x1f, - 0xa2, 0xd6, 0x46, 0x10, 0xac, 0xee, 0x57, 0x81, 0x27, 0xbc, 0x43, 0xfc, 0x1e, 0x52, 0x09, 0x01, - 0x54, 0xdd, 0x57, 0x01, 0xac, 0x60, 0x62, 0xcc, 0x79, 0xe6, 0x65, 0xca, 0xbc, 0x2c, 0x20, 0xbb, - 0xe7, 0xac, 0x07, 0xf5, 0xd1, 0x18, 0xf7, 0x3d, 0x0f, 0xad, 0xf6, 0x96, 0xe7, 0x08, 0xbf, 0xcd, - 0xed, 0x65, 0x4d, 0xfd, 0x7e, 0x9a, 0x8c, 0x87, 0x5a, 0x2e, 0xf1, 0xa2, 0xe5, 0x6c, 0x4f, 0xac, - 0xb1, 0xe5, 0x1f, 0x71, 0xad, 0xbf, 0x92, 0xba, 0x01, 0xc0, 0x31, 0xa4, 0xf6, 0x57, 0xec, 0x24, - 0x71, 0xad, 0x09, 0x17, 0xd6, 0x64, 0xfe, 0xad, 0x62, 0xfa, 0xfe, 0xd9, 0x26, 0x54, 0xa4, 0xfc, - 0xc9, 0x41, 0x6f, 0xcc, 0xc9, 0xc1, 0x14, 0x07, 0x2b, 0x97, 0x05, 0x84, 0xdf, 0xb7, 0x98, 0x18, - 0xa1, 0xe1, 0x78, 0x47, 0xd2, 0x4d, 0x12, 0x01, 0xe9, 0x9a, 0x74, 0x66, 0x66, 0xcc, 0xac, 0x60, - 0x6e, 0x4c, 0xa2, 0x15, 0x92, 0x00, 0x92, 0xf7, 0x4a, 0x72, 0xf2, 0x7b, 0x25, 0x0a, 0x3f, 0x5c, - 0xe5, 0xd1, 0x29, 0x74, 0x96, 0x8a, 0x76, 0xbd, 0x98, 0xb6, 0x58, 0x8a, 0x38, 0xff, 0x8b, 0x40, - 0x5d, 0x93, 0xdd, 0x81, 0xe4, 0x3d, 0x16, 0xdd, 0x08, 0x74, 0x6f, 0x1a, 0x85, 0xc6, 0xc5, 0xe0, - 0x66, 0x30, 0x98, 0xc6, 0x4e, 0xf9, 0x99, 0x67, 0xc6, 0xe3, 0x8b, 0x00, 0x92, 0x71, 0xf7, 0xe1, - 0x82, 0xf0, 0x14, 0x5a, 0xa6, 0x3e, 0xb5, 0x2d, 0xc7, 0xd4, 0x6d, 0x93, 0x1f, 0x5d, 0xe7, 0xb5, - 0xed, 0x18, 0xf5, 0x08, 0x31, 0x5d, 0x93, 0x54, 0x1b, 0x61, 0x1e, 0x9a, 0xf6, 0x91, 0x15, 0x84, - 0x22, 0x90, 0xab, 0xca, 0x81, 0x6d, 0x82, 0xa9, 0x4e, 0xb4, 0x44, 0x9a, 0x61, 0x48, 0x77, 0xbd, - 0x62, 0x3d, 0x2b, 0x13, 0x5d, 0x03, 0xe1, 0x2a, 0xd6, 0x2b, 0x14, 0x04, 0x25, 0x22, 0x74, 0xc5, - 0x4a, 0x89, 0x23, 0x6f, 0xef, 0xc0, 0x16, 0x22, 0x45, 0xa5, 0x88, 0x84, 0xdf, 0xff, 0xa9, 0x4d, - 0x3c, 0x87, 0x57, 0x08, 0x81, 0xea, 0x5f, 0xcd, 0x03, 0x24, 0x16, 0x36, 0x5d, 0x2d, 0x23, 0xd7, - 0x57, 0x3c, 0x72, 0x45, 0x4a, 0x77, 0x4d, 0xf6, 0x00, 0x2e, 0x8b, 0x8b, 0x6c, 0x71, 0x74, 0x85, - 0xed, 0xea, 0x63, 0x23, 0x8a, 0xe6, 0x62, 0x02, 0xcb, 0xcf, 0xcd, 0xbb, 0xee, 0xae, 0x81, 0xfa, - 0xfa, 0x96, 0xcc, 0x13, 0x9e, 0xcf, 0xd3, 0xae, 0x76, 0x59, 0x0b, 0x4c, 0xd8, 0x47, 0xe7, 0x73, - 0xf6, 0x1e, 0x5c, 0xf2, 0xad, 0xa9, 0x6f, 0x05, 0xc7, 0x7a, 0x18, 0xc8, 0x85, 0xf1, 0x70, 0xcb, - 0x6d, 0x81, 0x1c, 0x05, 0x71, 0x59, 0xef, 0xc1, 0x25, 0xd1, 0xb9, 0x4b, 0xd5, 0xe3, 0x2e, 0xec, - 0x6d, 0x8e, 0x94, 0x6b, 0x47, 0xb1, 0xb5, 0xe4, 0x76, 0x88, 0x5e, 0xc0, 0x29, 0x69, 0x65, 0xee, - 0x62, 0x10, 0xf7, 0xc2, 0xc9, 0x77, 0x40, 0x0b, 0xb7, 0xa4, 0xf1, 0x04, 0x53, 0x21, 0x87, 0x63, - 0x4f, 0x43, 0x57, 0x7f, 0x50, 0xbf, 0x4f, 0x2f, 0xfc, 0xd0, 0x3d, 0x7d, 0xcf, 0xb4, 0x34, 0xc2, - 0xb1, 0x77, 0x70, 0x5e, 0x24, 0xcd, 0x8e, 0x1e, 0xa9, 0xe0, 0x01, 0x06, 0x4a, 0xd2, 0x50, 0x8d, - 0x3f, 0x57, 0xf1, 0x16, 0x30, 0xa9, 0xe6, 0x11, 0x75, 0x95, 0xa8, 0xb7, 0xe2, 0x6a, 0x0b, 0xe2, - 0x37, 0x80, 0xaa, 0xc8, 0xcf, 0xaf, 0x6a, 0xab, 0x86, 0x36, 0x22, 0xe9, 0x28, 0xeb, 0x3d, 0xb8, - 0x94, 0xb4, 0x4e, 0x37, 0x42, 0x3d, 0x3c, 0xb6, 0x74, 0xcb, 0x35, 0xe9, 0xf6, 0x61, 0x49, 0xdb, - 0x8e, 0x1b, 0xda, 0x0c, 0x47, 0xc7, 0x16, 0x9a, 0xca, 0x92, 0x2b, 0x73, 0xeb, 0xc5, 0xae, 0xcc, - 0x8f, 0xa0, 0x91, 0x0a, 0xd7, 0x90, 0xbb, 0x9b, 0xdf, 0xde, 0xbd, 0x28, 0x07, 0x69, 0xc4, 0x3d, - 0x7e, 0x0f, 0xb6, 0x8f, 0x8d, 0x40, 0x4f, 0xf1, 0x92, 0x87, 0xb5, 0xa4, 0x6d, 0x1d, 0x1b, 0xc1, - 0x81, 0xc4, 0xa3, 0xfe, 0x7e, 0x06, 0xea, 0x69, 0x9f, 0x03, 0xbf, 0x12, 0xe5, 0x2c, 0x66, 0x2e, - 0x8f, 0x34, 0xcc, 0x6b, 0x51, 0x12, 0xd7, 0x02, 0x8f, 0xe4, 0x58, 0xcc, 0xdc, 0x68, 0x2d, 0xcc, - 0x4f, 0x5a, 0x94, 0x66, 0x6f, 0x42, 0x71, 0x7e, 0xc2, 0x25, 0xf4, 0xf3, 0x66, 0x5f, 0x61, 0xce, - 0x23, 0xcb, 0xdf, 0x84, 0xe2, 0x42, 0x90, 0xe6, 0x9e, 0x47, 0xba, 0x20, 0x52, 0xf5, 0x9f, 0x6f, - 0x42, 0x55, 0x76, 0x95, 0x7d, 0x93, 0x10, 0x8f, 0x6f, 0x15, 0x7d, 0xb3, 0x43, 0x41, 0xa2, 0x3a, - 0x85, 0xb1, 0x63, 0x3f, 0xf1, 0xf8, 0x0e, 0x38, 0x36, 0x82, 0xe6, 0x22, 0xf4, 0x50, 0xe4, 0xa3, - 0x28, 0xf5, 0x9c, 0x28, 0xbc, 0x9d, 0xaf, 0x0c, 0x94, 0x09, 0x22, 0xb2, 0xfd, 0x3d, 0x71, 0x7b, - 0x86, 0xee, 0xcb, 0x51, 0x3c, 0x65, 0x7e, 0x65, 0xbe, 0x54, 0xa3, 0xeb, 0x72, 0x14, 0x3f, 0xfa, - 0x00, 0xb6, 0x92, 0xbb, 0x0a, 0x9c, 0xa5, 0xb0, 0xc2, 0x52, 0x8b, 0x2f, 0x2a, 0x88, 0xbb, 0xfc, - 0x35, 0x3b, 0xd0, 0x3d, 0xc7, 0x8c, 0x2e, 0x45, 0x15, 0xa3, 0x83, 0x8c, 0x81, 0x63, 0x8a, 0x4b, - 0x9a, 0x9c, 0xc6, 0xb5, 0x4e, 0x23, 0x9a, 0xf8, 0xb0, 0xa3, 0x6f, 0x9d, 0x8a, 0xcb, 0x51, 0x7f, - 0x9e, 0x81, 0xed, 0x15, 0xef, 0x18, 0x8a, 0xf6, 0xe4, 0x65, 0x29, 0xfc, 0x89, 0xd6, 0xdd, 0xcc, - 0x08, 0x27, 0xc7, 0xfa, 0xdc, 0xb7, 0xa6, 0xf6, 0x59, 0xf4, 0x3c, 0x16, 0xc1, 0x0e, 0x08, 0x84, - 0xd2, 0x9f, 0x1f, 0xd8, 0xf1, 0xf3, 0x03, 0x2e, 0xf8, 0xf8, 0x21, 0x5d, 0x8f, 0x0e, 0x0e, 0xa2, - 0xf8, 0xd2, 0xdc, 0x73, 0xe2, 0x4b, 0xaf, 0x41, 0xd9, 0xf5, 0x28, 0x96, 0x68, 0x7e, 0x22, 0x82, - 0xb0, 0x8a, 0xae, 0x17, 0x0e, 0xdc, 0x83, 0x13, 0xf6, 0x00, 0x2e, 0x2d, 0x02, 0x3a, 0xb0, 0x1f, - 0x5b, 0x7e, 0x70, 0x6c, 0xc7, 0x76, 0x2a, 0x17, 0x20, 0x17, 0x16, 0x81, 0xb5, 0x1f, 0xe3, 0x78, - 0x4b, 0xd4, 0xeb, 0x50, 0xe8, 0xc6, 0x5e, 0xbd, 0x38, 0xa0, 0x2d, 0x2b, 0xde, 0x94, 0xf1, 0xa0, - 0xdc, 0xa2, 0xf7, 0x69, 0xf6, 0x8d, 0x39, 0xbb, 0x07, 0xd9, 0x99, 0x31, 0x17, 0xa7, 0x84, 0x8d, - 0xf8, 0x28, 0x95, 0x63, 0xef, 0xef, 0x1b, 0x73, 0xbe, 0xfb, 0x23, 0xd1, 0xb5, 0x8f, 0xa0, 0x14, - 0x01, 0xbe, 0xd5, 0x8e, 0xfd, 0x6f, 0x36, 0xa1, 0xdc, 0x96, 0xbd, 0xf2, 0x13, 0xc3, 0xd5, 0x43, - 0x7f, 0xe1, 0xa2, 0x02, 0x1d, 0xbd, 0xb4, 0x31, 0x31, 0xdc, 0x91, 0x00, 0x45, 0x53, 0x7b, 0xf3, - 0x05, 0x53, 0xfb, 0x3a, 0x80, 0x4f, 0x4e, 0x2d, 0xf2, 0x6b, 0x65, 0xe3, 0x00, 0xdf, 0xae, 0xd9, - 0x35, 0xcf, 0xd6, 0xc7, 0x36, 0xe5, 0xbe, 0x79, 0x6c, 0x53, 0x7e, 0x6d, 0x6c, 0xd3, 0x9d, 0x64, - 0x7b, 0xc1, 0x29, 0x8e, 0x05, 0x97, 0xf9, 0x26, 0x37, 0x8f, 0xef, 0x0a, 0x61, 0xe9, 0xdf, 0x83, - 0x7a, 0xd4, 0x3a, 0x91, 0x1f, 0xa4, 0xae, 0x27, 0x09, 0x1c, 0x77, 0xe3, 0xd7, 0x42, 0x39, 0x99, - 0x5e, 0xb2, 0x95, 0x97, 0xc4, 0x4f, 0xfd, 0xf5, 0x0c, 0x30, 0xe1, 0x84, 0x79, 0xb4, 0x70, 0x9c, - 0x91, 0x75, 0x46, 0x92, 0xe1, 0x1e, 0x6c, 0x8b, 0x53, 0x06, 0xe9, 0x5a, 0xa8, 0x38, 0x57, 0xe7, - 0x88, 0xe4, 0x5c, 0x7d, 0xdd, 0x0d, 0xd2, 0xcd, 0xb5, 0x37, 0x48, 0xd7, 0xdf, 0x4c, 0xbd, 0x09, - 0x15, 0xf9, 0xfe, 0x25, 0xd7, 0x93, 0xc0, 0x48, 0xae, 0x5e, 0xfe, 0xf9, 0x26, 0x40, 0xe2, 0x28, - 0xfa, 0x65, 0x07, 0xa6, 0xad, 0x19, 0x92, 0xec, 0xba, 0x21, 0xb9, 0x0b, 0x8a, 0x4c, 0x27, 0x5d, - 0x04, 0xae, 0x27, 0x84, 0x91, 0x9a, 0x63, 0x07, 0xf2, 0x65, 0x4d, 0xba, 0xf7, 0x21, 0x62, 0x79, - 0x38, 0x92, 0x7b, 0xad, 0xc5, 0x02, 0x2c, 0xd9, 0x01, 0x97, 0xc9, 0xec, 0x53, 0xb8, 0x1a, 0x73, - 0xea, 0xa7, 0x76, 0x78, 0xec, 0x2d, 0x42, 0xb1, 0x58, 0x03, 0x21, 0xa5, 0x2e, 0x47, 0x39, 0x3d, - 0xe5, 0x68, 0xbe, 0x5e, 0x03, 0xb4, 0x96, 0xa6, 0x0b, 0xc7, 0xd1, 0x43, 0xeb, 0x2c, 0x14, 0x8f, - 0x77, 0x34, 0x52, 0x3e, 0x36, 0x69, 0x78, 0xb5, 0xd2, 0x54, 0x24, 0xd4, 0xff, 0x99, 0x85, 0xfc, - 0x8f, 0x16, 0x96, 0x7f, 0xce, 0x3e, 0x82, 0x72, 0x10, 0xce, 0x42, 0xf9, 0xb8, 0xfb, 0x2a, 0xcf, - 0x80, 0xf0, 0x74, 0x5a, 0x6d, 0xcd, 0x2c, 0x37, 0xe4, 0xce, 0x67, 0xa4, 0xa5, 0x0d, 0xe8, 0x22, - 0xe4, 0x83, 0xd0, 0x9a, 0x07, 0x22, 0x68, 0x95, 0x27, 0xd8, 0x0e, 0xe4, 0x5d, 0xcf, 0xb4, 0x82, - 0x74, 0x68, 0x6a, 0x1f, 0x35, 0x0e, 0x8e, 0x60, 0x2a, 0x14, 0xe2, 0x11, 0x5f, 0x39, 0x72, 0xe6, - 0x18, 0xba, 0xf0, 0x62, 0x19, 0xa6, 0xed, 0x1e, 0x45, 0x17, 0xab, 0xe3, 0x34, 0x6e, 0xad, 0x64, - 0x65, 0x19, 0x47, 0xd1, 0x83, 0x0c, 0x22, 0xc9, 0x76, 0xa0, 0x82, 0x3f, 0x9f, 0xfa, 0x76, 0x68, - 0x0d, 0x1f, 0x46, 0xd2, 0x5d, 0x02, 0xa1, 0x8d, 0x64, 0x5a, 0xa1, 0x35, 0x09, 0x87, 0x5f, 0x8b, - 0x80, 0xcf, 0xb2, 0x26, 0x41, 0xd8, 0xf7, 0x80, 0x8d, 0x8d, 0xc9, 0xc9, 0x91, 0x4f, 0x61, 0x1a, - 0x5f, 0x2f, 0x2c, 0xdf, 0xb6, 0xa2, 0x00, 0xcf, 0x8a, 0xd4, 0x29, 0xda, 0x76, 0x42, 0xf6, 0x23, - 0x4e, 0x85, 0xd6, 0xdd, 0xcc, 0x38, 0x6b, 0x7b, 0x73, 0x11, 0x3f, 0x27, 0x52, 0xec, 0x21, 0x5c, - 0xc6, 0xcd, 0x31, 0xba, 0xe9, 0x8b, 0x73, 0x48, 0xb8, 0x35, 0xf9, 0xeb, 0x49, 0x17, 0x8e, 0x8d, - 0x20, 0xb9, 0xdc, 0xc7, 0x9d, 0x02, 0xea, 0x6f, 0x40, 0x2d, 0xd5, 0xef, 0x2b, 0x8e, 0xbd, 0x61, - 0xa7, 0xd7, 0x69, 0x8d, 0xb8, 0xa3, 0x40, 0x38, 0x9c, 0x36, 0x25, 0xcf, 0x54, 0x4e, 0x72, 0x20, - 0xe4, 0xc9, 0xaf, 0xd5, 0xd1, 0x1e, 0x77, 0x94, 0x82, 0x9a, 0x2b, 0x65, 0x95, 0xac, 0xfa, 0x37, - 0x36, 0x61, 0x7b, 0xe4, 0x1b, 0x6e, 0x60, 0x70, 0x1d, 0xc6, 0x0d, 0x7d, 0xcf, 0x61, 0xdf, 0x83, - 0x52, 0x38, 0x71, 0xe4, 0x89, 0x70, 0x33, 0x12, 0x3b, 0x4b, 0xa4, 0xf7, 0x47, 0x13, 0x7e, 0x16, - 0x51, 0x0c, 0xf9, 0x0f, 0xf6, 0x0e, 0xe4, 0xc7, 0xd6, 0x91, 0xed, 0x0a, 0xc9, 0x7b, 0x69, 0x99, - 0x71, 0x17, 0x91, 0x7b, 0x1b, 0x1a, 0xa7, 0x62, 0xef, 0x41, 0x61, 0xe2, 0xcd, 0xa2, 0x2d, 0x2f, - 0xb9, 0xe6, 0x27, 0x15, 0x84, 0xd8, 0xbd, 0x0d, 0x4d, 0xd0, 0xb1, 0x8f, 0xa0, 0xe4, 0x7b, 0x8e, - 0x83, 0xfd, 0x2e, 0x36, 0xc3, 0xc6, 0x32, 0x8f, 0x26, 0xf0, 0x7b, 0x1b, 0x5a, 0x4c, 0xab, 0xde, - 0x87, 0xa2, 0xa8, 0x2c, 0x76, 0xc3, 0x6e, 0xe7, 0x71, 0x57, 0xf4, 0x60, 0x6b, 0xb0, 0xbf, 0xdf, - 0x1d, 0xf1, 0x3b, 0xcc, 0xda, 0xa0, 0xd7, 0xdb, 0x6d, 0xb6, 0x3e, 0x57, 0x36, 0x77, 0x4b, 0x50, - 0xe0, 0xc3, 0xa3, 0xfe, 0x56, 0x06, 0xb6, 0x96, 0x1a, 0xc0, 0x3e, 0x81, 0x1c, 0xd9, 0x57, 0xbc, - 0x7b, 0x6e, 0xaf, 0x6d, 0xa5, 0x94, 0xe6, 0x9a, 0x36, 0x72, 0xa8, 0x9f, 0x42, 0x3d, 0x0d, 0x97, - 0x9c, 0x49, 0x35, 0x28, 0x6b, 0x9d, 0x66, 0x5b, 0x1f, 0xf4, 0x7b, 0x5f, 0x72, 0xdf, 0x2c, 0x25, - 0x9f, 0x6a, 0x5d, 0xf2, 0xff, 0xfc, 0x3a, 0x28, 0xcb, 0x1d, 0xc3, 0x1e, 0xa3, 0xd5, 0x34, 0x9b, - 0x3b, 0x16, 0x29, 0xa7, 0xd2, 0x90, 0xdd, 0x58, 0xd3, 0x93, 0x82, 0x8c, 0xc7, 0x17, 0x4d, 0x52, - 0x69, 0xf5, 0x37, 0x80, 0xad, 0xf6, 0xe0, 0x2f, 0x2f, 0xfb, 0xff, 0x91, 0x81, 0xdc, 0x81, 0x63, - 0xb8, 0xec, 0x16, 0xe4, 0xe9, 0x99, 0x20, 0x21, 0xbf, 0xe5, 0xd5, 0x84, 0xd3, 0x82, 0x70, 0xec, - 0x2d, 0xc8, 0x86, 0x93, 0xe8, 0xea, 0xf4, 0x95, 0xe7, 0x4c, 0xbe, 0xbd, 0x0d, 0x0d, 0xa9, 0xd8, - 0x5d, 0xc8, 0x9a, 0x66, 0x74, 0xdd, 0x40, 0xb8, 0x94, 0xda, 0x46, 0x68, 0xb4, 0xad, 0xa9, 0xed, - 0xda, 0xe2, 0x59, 0x23, 0x24, 0x61, 0xaf, 0x43, 0xd6, 0x9c, 0x38, 0xe9, 0xbb, 0x23, 0xdc, 0x89, - 0x10, 0x67, 0x68, 0x4e, 0x1c, 0xd4, 0xfd, 0x42, 0xff, 0x5c, 0xf7, 0x17, 0x2e, 0x05, 0xaa, 0x06, - 0xc2, 0x1e, 0xab, 0xa0, 0x16, 0xb3, 0xa0, 0x68, 0xd7, 0x40, 0xdc, 0xc1, 0x9c, 0xfb, 0xd6, 0xdc, - 0xf0, 0x63, 0x4b, 0xcc, 0x0e, 0x0e, 0x38, 0x60, 0xb7, 0x00, 0xf4, 0xfa, 0xaa, 0xfa, 0x36, 0xbd, - 0x61, 0x83, 0x2a, 0xbd, 0x1a, 0xfd, 0x5a, 0xf3, 0x50, 0x9f, 0xc0, 0xa8, 0x7f, 0x91, 0x85, 0x8a, - 0x54, 0x1f, 0xf6, 0x01, 0x94, 0xcc, 0xf4, 0x42, 0xbc, 0xba, 0x52, 0xe9, 0xfb, 0xed, 0x68, 0x09, - 0x9a, 0x62, 0x7a, 0xd3, 0x41, 0x57, 0xa8, 0x3f, 0x33, 0x7c, 0x9b, 0xbf, 0x5c, 0xb6, 0x29, 0x9f, - 0x38, 0x0d, 0xad, 0xf0, 0x49, 0x84, 0xd9, 0xdb, 0xd0, 0xaa, 0x81, 0x94, 0x26, 0xbb, 0x43, 0x34, - 0x29, 0x9b, 0x7a, 0x01, 0x8e, 0x03, 0xf7, 0x36, 0xb4, 0x08, 0x8f, 0xa4, 0xd6, 0x99, 0x35, 0x59, - 0x84, 0x91, 0xdd, 0x51, 0x8b, 0x1a, 0x44, 0x40, 0x7a, 0x86, 0x92, 0xff, 0x64, 0x0f, 0x50, 0xda, - 0x1a, 0x8e, 0xe3, 0x91, 0xb2, 0x96, 0x97, 0xcf, 0x7f, 0xda, 0x31, 0x9c, 0x3f, 0x7b, 0x19, 0xa5, - 0xd8, 0x1d, 0xc8, 0x7b, 0xe1, 0xb1, 0x15, 0x69, 0xf3, 0xd1, 0x6b, 0x38, 0x08, 0x6a, 0xb7, 0x7a, - 0x38, 0x53, 0x08, 0xad, 0xfe, 0x41, 0x06, 0x8a, 0xa2, 0x07, 0xd8, 0x36, 0xd4, 0x86, 0x9d, 0x91, - 0xfe, 0xa4, 0xa9, 0x75, 0x9b, 0xbb, 0xbd, 0x8e, 0xb8, 0xf2, 0xf2, 0x58, 0x6b, 0xf6, 0x85, 0x80, - 0xd4, 0x3a, 0x4f, 0x06, 0x9f, 0x77, 0xb8, 0x87, 0xb6, 0xdd, 0xe9, 0x7f, 0xa9, 0x64, 0xf9, 0x71, - 0x44, 0xe7, 0xa0, 0xa9, 0xa1, 0xac, 0xac, 0x40, 0xb1, 0xf3, 0x45, 0xa7, 0x75, 0x48, 0xc2, 0xb2, - 0x0e, 0xd0, 0xee, 0x34, 0x7b, 0xbd, 0x41, 0x0b, 0x85, 0x67, 0x81, 0x31, 0xa8, 0xb7, 0xb4, 0x4e, - 0x73, 0xd4, 0xd1, 0x9b, 0xad, 0xd6, 0xe0, 0xb0, 0x3f, 0x52, 0x8a, 0x58, 0x62, 0xb3, 0x37, 0xea, - 0x68, 0x31, 0x88, 0x5e, 0x28, 0x6b, 0x6b, 0x83, 0x83, 0x18, 0x52, 0xde, 0x2d, 0xa3, 0x0d, 0x48, - 0x63, 0xa5, 0xfe, 0xd1, 0x36, 0xd4, 0xd3, 0x53, 0x93, 0x7d, 0x0c, 0x25, 0xd3, 0x4c, 0x8d, 0xf1, - 0xf5, 0x75, 0x53, 0xf8, 0x7e, 0xdb, 0x8c, 0x86, 0x99, 0xff, 0x60, 0xaf, 0x45, 0x0b, 0x69, 0x73, - 0x65, 0x21, 0x45, 0xcb, 0xe8, 0x07, 0xb0, 0x25, 0x9e, 0x68, 0x31, 0x8d, 0xd0, 0x18, 0x1b, 0x81, - 0x95, 0x5e, 0x25, 0x2d, 0x42, 0xb6, 0x05, 0x6e, 0x6f, 0x43, 0xab, 0x4f, 0x52, 0x10, 0xf6, 0x7d, - 0xa8, 0x1b, 0xdc, 0xfb, 0x13, 0xf1, 0xe7, 0x64, 0x35, 0xb4, 0x49, 0x4e, 0xa0, 0x84, 0xbd, 0x66, - 0xc8, 0x00, 0x9c, 0x88, 0xa6, 0xef, 0xcd, 0x13, 0xe6, 0x7c, 0xea, 0xe8, 0xd3, 0xf7, 0xe6, 0x12, - 0x6f, 0xd5, 0x94, 0xd2, 0xec, 0x23, 0xa8, 0x8a, 0x9a, 0x27, 0xae, 0x8e, 0x78, 0xc9, 0xf2, 0x6a, - 0x93, 0x5a, 0xb9, 0xb7, 0xa1, 0x55, 0x26, 0x49, 0x92, 0x3d, 0x44, 0x5d, 0x32, 0x51, 0xc2, 0x8b, - 0xf2, 0x5c, 0xa3, 0xda, 0x46, 0x5c, 0x60, 0xc4, 0x29, 0xf6, 0x1e, 0x00, 0xd5, 0x93, 0xf3, 0x94, - 0x52, 0x41, 0x41, 0xbe, 0x37, 0x8f, 0x58, 0xca, 0x66, 0x94, 0x90, 0xaa, 0xc7, 0x1d, 0x55, 0xe5, - 0xd5, 0xea, 0x91, 0xb3, 0x2a, 0xa9, 0x1e, 0xf7, 0x71, 0xc5, 0xd5, 0xe3, 0x6c, 0xb0, 0x52, 0xbd, - 0x88, 0x8b, 0x57, 0x8f, 0x33, 0x45, 0xd5, 0xe3, 0x3c, 0x95, 0xe5, 0xea, 0x45, 0x2c, 0x54, 0x3d, - 0xce, 0xf1, 0xfd, 0x15, 0xeb, 0xa1, 0xfa, 0x5c, 0xeb, 0x01, 0x87, 0x2d, 0x6d, 0x3f, 0x7c, 0x1f, - 0xea, 0xc1, 0xb1, 0x77, 0x2a, 0x09, 0x90, 0x9a, 0xcc, 0x3d, 0x3c, 0xf6, 0x4e, 0x65, 0x09, 0x52, - 0x0b, 0x64, 0x00, 0xd6, 0x96, 0x37, 0x91, 0x8e, 0x29, 0xeb, 0x72, 0x6d, 0xa9, 0x85, 0x4f, 0x6c, - 0xeb, 0x14, 0x6b, 0x6b, 0x44, 0x09, 0xec, 0x94, 0xc4, 0xed, 0x13, 0x08, 0x47, 0x4e, 0x2a, 0x94, - 0x46, 0x94, 0x04, 0xb1, 0x03, 0x28, 0xc0, 0xb9, 0xb5, 0x70, 0x65, 0x36, 0x45, 0x9e, 0x5b, 0x87, - 0x6e, 0x8a, 0xb1, 0xca, 0x49, 0x05, 0x6b, 0xb2, 0x2a, 0x02, 0xeb, 0xeb, 0x85, 0xe5, 0x4e, 0x2c, - 0x11, 0x32, 0x97, 0x5a, 0x15, 0x43, 0x81, 0x4b, 0x56, 0x45, 0x04, 0x89, 0xe7, 0x75, 0xcc, 0xce, - 0x96, 0xe7, 0xb5, 0xc4, 0x4c, 0xf3, 0x3a, 0x66, 0x8d, 0x17, 0x54, 0xcc, 0x7b, 0x61, 0x65, 0x41, - 0x49, 0xcc, 0x7c, 0x41, 0xc5, 0xdc, 0x0f, 0x41, 0xcc, 0x26, 0xde, 0xb9, 0xa9, 0xc0, 0x3a, 0x5e, - 0x6b, 0xd1, 0xbb, 0x30, 0x89, 0x53, 0x38, 0x57, 0x7d, 0x0b, 0xad, 0x15, 0x31, 0x15, 0x2e, 0xc9, - 0x73, 0x55, 0x23, 0x4c, 0xbc, 0x94, 0xfc, 0x24, 0x29, 0x15, 0x36, 0xb7, 0x43, 0xbf, 0x61, 0xae, - 0x16, 0x76, 0x60, 0x87, 0x7e, 0x52, 0x18, 0xa6, 0xd8, 0x3b, 0x40, 0xd3, 0x90, 0xb3, 0x58, 0xb2, - 0xe8, 0xc6, 0x6e, 0x11, 0x0c, 0x25, 0x53, 0xfc, 0xc6, 0xc9, 0x22, 0xca, 0x98, 0x98, 0x93, 0xc6, - 0x54, 0x9e, 0x2c, 0xbc, 0x88, 0x56, 0xbb, 0x85, 0x93, 0x85, 0x13, 0xb5, 0xcc, 0x09, 0xbb, 0x07, - 0xc4, 0x4d, 0xf4, 0x47, 0xa9, 0xd7, 0xd6, 0x7c, 0x6f, 0xce, 0xa9, 0x8b, 0x48, 0x80, 0xb4, 0xd8, - 0x02, 0xc7, 0x73, 0xa3, 0x86, 0x1f, 0xa7, 0x5a, 0x80, 0x88, 0x58, 0x18, 0x4c, 0xe2, 0x94, 0xfa, - 0x3b, 0x05, 0x28, 0x0a, 0x59, 0xcb, 0x2e, 0xc0, 0x96, 0x10, 0xf9, 0xed, 0xe6, 0xa8, 0xb9, 0xdb, - 0x1c, 0xa2, 0x92, 0xc6, 0xa0, 0xce, 0x65, 0x7e, 0x0c, 0xcb, 0xe0, 0x3e, 0x40, 0x42, 0x3f, 0x06, - 0x6d, 0xe2, 0x3e, 0x20, 0x78, 0xf9, 0xab, 0x96, 0x59, 0xb6, 0x05, 0x15, 0xce, 0xc8, 0x01, 0x74, - 0x03, 0x97, 0xb8, 0x78, 0x3a, 0x2f, 0xb1, 0xf0, 0x03, 0xcb, 0x42, 0xc2, 0xc2, 0x01, 0xc5, 0x98, - 0x25, 0x3a, 0xd1, 0x64, 0x50, 0x1f, 0x69, 0x87, 0xfd, 0x56, 0x52, 0x4e, 0x99, 0x6e, 0x4d, 0xf2, - 0x6c, 0x9e, 0x74, 0x3b, 0x4f, 0x15, 0x40, 0x26, 0x9e, 0x0b, 0xa5, 0x2b, 0xa8, 0x66, 0x52, 0x26, - 0x94, 0xac, 0xb2, 0x2b, 0x70, 0x61, 0xb8, 0x37, 0x78, 0xaa, 0x73, 0xa6, 0xb8, 0x09, 0x35, 0x76, - 0x11, 0x14, 0x09, 0xc1, 0xb3, 0xaf, 0x63, 0x91, 0x04, 0x8d, 0x08, 0x87, 0xca, 0x16, 0x1d, 0xfe, - 0x23, 0x6c, 0xc4, 0xf7, 0x5d, 0x05, 0x9b, 0xc2, 0x59, 0x07, 0xbd, 0xc3, 0xfd, 0xfe, 0x50, 0xd9, - 0xc6, 0x4a, 0x10, 0x84, 0xd7, 0x9c, 0xc5, 0xd9, 0x24, 0xbb, 0xf5, 0x05, 0xda, 0xc0, 0x11, 0xf6, - 0xb4, 0xa9, 0xf5, 0xbb, 0xfd, 0xc7, 0x43, 0xe5, 0x62, 0x9c, 0x73, 0x47, 0xd3, 0x06, 0xda, 0x50, - 0xb9, 0x14, 0x03, 0x86, 0xa3, 0xe6, 0xe8, 0x70, 0xa8, 0x5c, 0x8e, 0x6b, 0x79, 0xa0, 0x0d, 0x5a, - 0x9d, 0xe1, 0xb0, 0xd7, 0x1d, 0x8e, 0x94, 0x2b, 0xec, 0x12, 0x6c, 0x27, 0x35, 0x8a, 0x88, 0x1b, - 0x52, 0x45, 0xb5, 0xc7, 0x9d, 0x91, 0x72, 0x35, 0xae, 0x46, 0x6b, 0xd0, 0xeb, 0x35, 0xe9, 0x64, - 0xfb, 0x1a, 0x12, 0xd1, 0x61, 0xbe, 0x68, 0xcd, 0x2b, 0x58, 0xaf, 0xc3, 0xbe, 0x0c, 0xba, 0x2e, - 0x4d, 0x8d, 0x61, 0xe7, 0x47, 0x87, 0x9d, 0x7e, 0xab, 0xa3, 0xbc, 0x9a, 0x4c, 0x8d, 0x18, 0x76, - 0x23, 0x9e, 0x1a, 0x31, 0xe8, 0x66, 0x5c, 0x66, 0x04, 0x1a, 0x2a, 0x3b, 0x98, 0x9f, 0xa8, 0x47, - 0xbf, 0xdf, 0x69, 0x8d, 0xb0, 0xad, 0xaf, 0xc5, 0xbd, 0x78, 0x78, 0xf0, 0x58, 0x6b, 0xb6, 0x3b, - 0x8a, 0x8a, 0x10, 0xad, 0xd3, 0x6f, 0xee, 0x47, 0xa3, 0x7d, 0x4b, 0x1a, 0xed, 0x83, 0xee, 0x48, - 0x53, 0x6e, 0xc7, 0xa3, 0x4b, 0xc9, 0xd7, 0xd9, 0x2b, 0x70, 0x45, 0x9e, 0x87, 0xfa, 0xd3, 0xee, - 0x68, 0x4f, 0x1c, 0xc4, 0xdf, 0xe1, 0x87, 0xc8, 0x84, 0x6c, 0xb5, 0x5b, 0x3c, 0xe2, 0x80, 0x78, - 0x31, 0x75, 0x77, 0xb7, 0x4a, 0x8f, 0x93, 0x0b, 0x05, 0x44, 0xfd, 0x0c, 0x98, 0xfc, 0x4e, 0xaf, - 0x88, 0x3c, 0x66, 0x90, 0x9b, 0xfa, 0xde, 0x2c, 0x7a, 0x0d, 0x03, 0x7f, 0xa3, 0xfd, 0x3d, 0x5f, - 0x8c, 0xe9, 0x70, 0x3b, 0xb9, 0xed, 0x2e, 0x83, 0xd4, 0xbf, 0x9b, 0x81, 0x7a, 0x5a, 0xf9, 0x20, - 0x87, 0xeb, 0x54, 0x77, 0xbd, 0x90, 0x3f, 0x2f, 0x16, 0xc4, 0xcf, 0xe7, 0x4e, 0xfb, 0x5e, 0x48, - 0xef, 0x8b, 0x91, 0x3b, 0x20, 0xd6, 0x25, 0x78, 0xae, 0x71, 0x9a, 0x75, 0xe1, 0x42, 0xea, 0xa9, - 0xe3, 0xd4, 0xe3, 0x6e, 0x8d, 0xf8, 0x89, 0xd2, 0xa5, 0xfa, 0x6b, 0x2c, 0x58, 0x6d, 0x93, 0x78, - 0xb3, 0x20, 0x97, 0xbc, 0x59, 0xb0, 0x07, 0xb5, 0x94, 0xae, 0x43, 0x5e, 0x9c, 0x69, 0xba, 0xa6, - 0x25, 0x7b, 0xfa, 0xf2, 0x6a, 0xaa, 0x7f, 0x3b, 0x03, 0x55, 0x59, 0xf3, 0xf9, 0xce, 0x39, 0x51, - 0x40, 0x90, 0xf8, 0xad, 0xdb, 0x66, 0xf4, 0xac, 0x58, 0x04, 0xea, 0xd2, 0x47, 0x19, 0xb8, 0xef, - 0xfa, 0xd1, 0xc9, 0x30, 0x6e, 0x8e, 0x0c, 0x62, 0x37, 0x00, 0xe8, 0xc9, 0x9f, 0x47, 0x9f, 0x23, - 0x81, 0x78, 0x93, 0x3a, 0x81, 0xa8, 0x37, 0xa1, 0xfc, 0xe8, 0x24, 0x8a, 0x8f, 0x92, 0x1f, 0xd9, - 0x2b, 0xf3, 0x27, 0x12, 0xd4, 0x3f, 0xcc, 0x40, 0x3d, 0xf1, 0x59, 0x50, 0x58, 0x01, 0x7f, 0x22, - 0x9b, 0x4f, 0x87, 0x4d, 0x73, 0x9c, 0x9c, 0x7f, 0x6e, 0xca, 0xe7, 0x9f, 0xb7, 0x44, 0x66, 0x59, - 0x59, 0xe4, 0xc7, 0x65, 0x89, 0x07, 0x18, 0x1e, 0x42, 0x15, 0xff, 0x6b, 0xd6, 0xd4, 0xf2, 0x7d, - 0xcb, 0x4c, 0xdf, 0x07, 0x49, 0x88, 0x53, 0x44, 0x64, 0xe3, 0x89, 0x93, 0xd5, 0xe7, 0xbc, 0x99, - 0x44, 0x6f, 0x79, 0xfd, 0xb7, 0x2c, 0x54, 0x24, 0x3d, 0xf2, 0x1b, 0x4d, 0xbf, 0xeb, 0x50, 0x4e, - 0x5e, 0xf0, 0x11, 0xb7, 0xde, 0x63, 0x40, 0x6a, 0xac, 0xb2, 0x4b, 0x63, 0xd5, 0x80, 0xa2, 0xcf, - 0x6f, 0xa9, 0x0a, 0x17, 0x72, 0x94, 0x4c, 0x3b, 0x6b, 0xf3, 0x2f, 0x39, 0x5f, 0x79, 0x1f, 0xaa, - 0x92, 0xa7, 0x35, 0x68, 0x14, 0xe4, 0x67, 0x9c, 0x62, 0xfa, 0x4a, 0xe2, 0x75, 0x0d, 0xd8, 0x25, - 0x28, 0x4c, 0x4f, 0x74, 0x73, 0xcc, 0xef, 0x42, 0x97, 0xb5, 0xfc, 0xf4, 0xa4, 0x3d, 0xa6, 0xd3, - 0xa7, 0x69, 0xac, 0x3a, 0x71, 0xff, 0x57, 0x69, 0x1a, 0x29, 0x48, 0x77, 0xa1, 0x38, 0x3d, 0x91, - 0xef, 0x34, 0xaf, 0x74, 0x79, 0x61, 0x7a, 0x42, 0x37, 0x99, 0xdf, 0x85, 0x8b, 0x62, 0xff, 0x36, - 0x02, 0x9d, 0x3f, 0x30, 0x42, 0x2f, 0x3b, 0xf1, 0x27, 0xf7, 0xb6, 0x39, 0xae, 0x19, 0x0c, 0x09, - 0x83, 0x33, 0x4e, 0x85, 0xaa, 0x34, 0x01, 0xf9, 0x13, 0x58, 0x65, 0x2d, 0x05, 0x63, 0x9f, 0x40, - 0x75, 0x7a, 0xc2, 0x07, 0x74, 0xe4, 0xed, 0x5b, 0xe2, 0x8a, 0xc6, 0xc5, 0xe5, 0xa1, 0xa4, 0xb8, - 0x8f, 0x14, 0x25, 0xbb, 0x0c, 0x05, 0xcd, 0x38, 0x1d, 0xfe, 0xa8, 0x47, 0x4a, 0x64, 0x59, 0x13, - 0xa9, 0xcf, 0x72, 0xa5, 0xba, 0xb2, 0xa5, 0xfe, 0xe3, 0x0c, 0xd4, 0x13, 0x1b, 0x00, 0x17, 0x21, - 0xbb, 0x27, 0xbf, 0x6d, 0xdf, 0x58, 0x36, 0x13, 0x90, 0xe4, 0xfe, 0xe8, 0x7c, 0xce, 0x5f, 0x80, - 0x5d, 0xf7, 0x6c, 0xd9, 0x3a, 0xd7, 0x77, 0x76, 0xed, 0xab, 0xda, 0x8f, 0x21, 0x3b, 0x3a, 0x9f, - 0x73, 0x7f, 0x13, 0x6e, 0x89, 0xdc, 0x36, 0xe5, 0x9b, 0x21, 0x05, 0x19, 0x7d, 0xde, 0xf9, 0x92, - 0xbf, 0xd2, 0x71, 0xa0, 0x75, 0xf7, 0x9b, 0xda, 0x97, 0x3a, 0x02, 0x48, 0x69, 0x78, 0x34, 0xd0, - 0x3a, 0xdd, 0xc7, 0x7d, 0x02, 0xe4, 0xc8, 0x1b, 0x95, 0x54, 0xb1, 0x69, 0x9a, 0x8f, 0x4e, 0xe4, - 0x17, 0x9f, 0x32, 0xa9, 0x17, 0x9f, 0xe2, 0xc7, 0xd1, 0xe4, 0x97, 0x22, 0xc3, 0xd8, 0x1f, 0xcf, - 0xe2, 0x55, 0x18, 0x2f, 0x69, 0xf6, 0x06, 0xe4, 0xa6, 0x27, 0xd6, 0x79, 0xda, 0xd0, 0x4b, 0x2f, - 0x20, 0x22, 0x50, 0x7f, 0x9e, 0x01, 0x96, 0xaa, 0x08, 0xb7, 0x3d, 0xbe, 0x6b, 0x5d, 0x3e, 0x86, - 0x86, 0x08, 0x81, 0xe4, 0x54, 0x92, 0xaf, 0x5d, 0x74, 0xe9, 0x25, 0x2f, 0x09, 0x0d, 0x4e, 0x5e, - 0x56, 0x63, 0xef, 0x02, 0x7f, 0x04, 0x92, 0xc2, 0x84, 0x72, 0xcf, 0xb1, 0x13, 0xb5, 0x84, 0x26, - 0x79, 0xf5, 0x51, 0x7e, 0xcd, 0x92, 0xbb, 0xe9, 0xb7, 0x92, 0x51, 0xa3, 0x35, 0xaf, 0xfe, 0x5e, - 0x06, 0x2e, 0xa4, 0x27, 0xc4, 0x2f, 0xd6, 0xca, 0xf4, 0xd3, 0x9d, 0xd9, 0xe5, 0xa7, 0x3b, 0xd7, - 0xcd, 0xa7, 0xdc, 0xda, 0xf9, 0xf4, 0xdb, 0x19, 0xb8, 0x28, 0xf5, 0x7e, 0x62, 0x2d, 0xfe, 0x25, - 0xd5, 0x4c, 0x7a, 0xc1, 0x33, 0x97, 0x7a, 0xc1, 0x53, 0xfd, 0xe3, 0x0c, 0x5c, 0x5e, 0xaa, 0x89, - 0x66, 0xfd, 0xa5, 0xd6, 0x25, 0xfd, 0xd2, 0x27, 0x1d, 0x15, 0x44, 0x2f, 0x59, 0x64, 0xee, 0x66, - 0xa5, 0x97, 0x3e, 0xe9, 0xfc, 0x88, 0x0e, 0x31, 0x5f, 0x15, 0x0f, 0x1e, 0xe9, 0xc1, 0xb9, 0x3b, - 0x11, 0x83, 0x5d, 0x26, 0xc8, 0xf0, 0xdc, 0x9d, 0xa8, 0x7f, 0x94, 0x81, 0xab, 0x4b, 0x6d, 0x68, - 0x2e, 0x42, 0x4f, 0x9c, 0x05, 0xff, 0x25, 0x35, 0xe3, 0x26, 0x54, 0xe8, 0xa4, 0x5c, 0x1c, 0x30, - 0xf3, 0x6e, 0x05, 0x23, 0x29, 0x57, 0x81, 0xac, 0x69, 0x9c, 0x8b, 0xd7, 0x02, 0xf0, 0x27, 0x2e, - 0xd8, 0x63, 0x6f, 0xe1, 0x8b, 0xe8, 0x20, 0xfa, 0xad, 0x7e, 0x00, 0xdb, 0x49, 0xd5, 0x5b, 0xe2, - 0xc1, 0xd5, 0x9b, 0x50, 0x71, 0xad, 0x53, 0x3d, 0x7a, 0x8e, 0x55, 0xc4, 0xbc, 0xb9, 0xd6, 0xa9, - 0x20, 0x50, 0x1f, 0xc9, 0xb2, 0x30, 0xfe, 0x8c, 0x84, 0x63, 0xa6, 0xe2, 0x76, 0x3c, 0xc7, 0x8c, - 0x50, 0x98, 0x9b, 0xd4, 0xca, 0xa2, 0x6b, 0x9d, 0xd2, 0x3c, 0x3c, 0x15, 0xf9, 0x34, 0x4d, 0x53, - 0xc4, 0x2e, 0xac, 0x7b, 0x1f, 0xed, 0x2a, 0x94, 0xe6, 0x7e, 0xaa, 0x9b, 0x8a, 0x73, 0x9f, 0x17, - 0x7b, 0x5b, 0x44, 0xd4, 0x3d, 0x2f, 0xce, 0x81, 0xc7, 0xd8, 0x89, 0xcf, 0xcc, 0xe4, 0x92, 0xcf, - 0xcc, 0x7c, 0x28, 0xc4, 0x20, 0xd9, 0x7d, 0xbc, 0x64, 0x05, 0xb2, 0xb6, 0x79, 0x46, 0x05, 0xd7, - 0x34, 0xfc, 0x49, 0x9a, 0x9c, 0xf5, 0xb5, 0x08, 0xea, 0xc3, 0x9f, 0xea, 0x2e, 0x54, 0xb4, 0x94, - 0x91, 0x5b, 0x95, 0xfc, 0x45, 0x41, 0xfa, 0x09, 0xa9, 0xa4, 0x83, 0xb4, 0x4a, 0xe2, 0x2e, 0x0a, - 0xd4, 0x40, 0x08, 0xbe, 0x27, 0x86, 0x3f, 0x39, 0x36, 0xfc, 0x9e, 0xe5, 0x1e, 0x85, 0xc7, 0xd8, - 0xe5, 0xdc, 0x8d, 0x2b, 0x77, 0x21, 0x70, 0x50, 0x34, 0x1d, 0xb0, 0x17, 0x1d, 0x22, 0x8f, 0x3e, - 0x60, 0xe1, 0x5a, 0xa7, 0x82, 0xff, 0x55, 0x00, 0xec, 0x7f, 0x81, 0xe6, 0x47, 0x90, 0x65, 0xcf, - 0x31, 0x39, 0x5a, 0xdd, 0x16, 0xed, 0x15, 0xaf, 0x5d, 0xb4, 0xad, 0xa9, 0xea, 0x88, 0x91, 0xe7, - 0x0d, 0x12, 0x9d, 0xf0, 0x9d, 0x86, 0x91, 0xbd, 0x06, 0xd5, 0xc8, 0x23, 0x41, 0xaf, 0x96, 0xf1, - 0xe2, 0x2b, 0x11, 0xac, 0xbf, 0x98, 0xa9, 0xbf, 0x9f, 0x85, 0x6a, 0x93, 0x47, 0xf6, 0xcc, 0xcf, - 0x07, 0xf3, 0x90, 0xfd, 0x06, 0x5c, 0xa2, 0x67, 0x4c, 0xf8, 0x43, 0xc2, 0x14, 0x50, 0x43, 0x71, - 0xf1, 0xa2, 0x13, 0xef, 0x49, 0x9d, 0x28, 0x58, 0xee, 0x0f, 0x4f, 0xec, 0x39, 0xbf, 0x8e, 0xd1, - 0x35, 0xcf, 0xe8, 0xee, 0x03, 0x8f, 0x0d, 0xa0, 0x17, 0x4f, 0xd2, 0x08, 0x7a, 0xf4, 0x00, 0xb3, - 0x9f, 0x9f, 0x88, 0x6c, 0x45, 0xd8, 0x04, 0x02, 0x0f, 0x4e, 0x38, 0xcd, 0x3d, 0xd8, 0xe6, 0x37, - 0xb0, 0x56, 0x37, 0xe0, 0x2d, 0x8e, 0x48, 0xe6, 0xf7, 0x10, 0xb6, 0xc5, 0xab, 0x2b, 0xf4, 0xbc, - 0xa6, 0x3e, 0xf1, 0xe6, 0xe7, 0xe2, 0xe8, 0xf1, 0x8d, 0xe7, 0x54, 0xb5, 0xcb, 0x49, 0x11, 0xc4, - 0xeb, 0xb9, 0x15, 0xa4, 0xa1, 0xd7, 0x3a, 0x70, 0xe5, 0x39, 0x6d, 0x7a, 0x59, 0x78, 0x43, 0x49, - 0x0a, 0x6f, 0xb8, 0xb6, 0x0b, 0x17, 0xd7, 0x95, 0xf7, 0x6d, 0xf2, 0x50, 0x7f, 0x56, 0x03, 0x48, - 0x66, 0x6c, 0x4a, 0x1d, 0xcd, 0x2c, 0xa9, 0xa3, 0xdf, 0x2a, 0xa8, 0xe7, 0x03, 0xa8, 0x63, 0x57, - 0xe9, 0x09, 0x47, 0x76, 0x2d, 0x47, 0x15, 0xa9, 0x46, 0xc9, 0x25, 0xd3, 0xd5, 0x90, 0x88, 0xdc, - 0xda, 0x90, 0x88, 0xf7, 0xa1, 0xc8, 0x0f, 0xda, 0x02, 0x71, 0xaf, 0xf9, 0xca, 0xf2, 0xea, 0xbb, - 0x2f, 0xae, 0x76, 0x44, 0x74, 0xac, 0x03, 0x75, 0x14, 0xfd, 0xbe, 0x1d, 0x1e, 0xcf, 0xe4, 0x5b, - 0xce, 0x37, 0x56, 0x39, 0x23, 0x32, 0xfe, 0x28, 0xa7, 0x21, 0x27, 0x25, 0xed, 0x35, 0x9c, 0x09, - 0xef, 0x2f, 0x69, 0xaf, 0x45, 0x59, 0x7b, 0x1d, 0xcd, 0xb8, 0xcf, 0x17, 0xb5, 0xd7, 0x77, 0xe0, - 0x82, 0xb8, 0x6e, 0x86, 0x0c, 0xd8, 0x9d, 0x44, 0xcf, 0x83, 0x2c, 0x15, 0xf1, 0xd8, 0xc9, 0x8c, - 0x6c, 0x3b, 0x24, 0xff, 0x02, 0x2e, 0x4e, 0x8e, 0x0d, 0xf7, 0xc8, 0xd2, 0xc3, 0xb1, 0xa3, 0xd3, - 0xd7, 0x0b, 0xf4, 0x99, 0x31, 0x17, 0x4a, 0xf5, 0x1b, 0x2b, 0x95, 0x6d, 0x11, 0xf1, 0x68, 0xec, - 0x50, 0x00, 0x5a, 0x1c, 0x38, 0xb3, 0x3d, 0x59, 0x86, 0x2f, 0x9d, 0x5f, 0xc3, 0xca, 0xf9, 0xf5, - 0xb2, 0x9a, 0x5d, 0x59, 0xa3, 0x66, 0x27, 0xca, 0x72, 0x55, 0x56, 0x96, 0xd9, 0xdb, 0x50, 0x14, - 0xb7, 0x65, 0x85, 0xdf, 0x97, 0xad, 0xae, 0x0e, 0x2d, 0x22, 0xc1, 0x92, 0xa2, 0x68, 0x0a, 0x7a, - 0x50, 0xa1, 0xce, 0x4b, 0x92, 0x61, 0x6c, 0x57, 0x38, 0x3d, 0xe3, 0x60, 0x39, 0xe1, 0xe3, 0xbd, - 0x26, 0x65, 0x1c, 0xe3, 0x84, 0x5d, 0xbe, 0xc4, 0x71, 0xed, 0x1f, 0x15, 0xa0, 0x20, 0x02, 0xe5, - 0xef, 0x41, 0xce, 0xf4, 0xbd, 0x79, 0x1c, 0x6d, 0xbe, 0x46, 0x6b, 0xa7, 0x0f, 0xd6, 0xa1, 0x82, - 0x7f, 0x1f, 0x0a, 0x86, 0x69, 0xea, 0xd3, 0x93, 0xf4, 0x79, 0xf4, 0x92, 0x02, 0xbd, 0xb7, 0xa1, - 0xe5, 0x0d, 0xd2, 0xa4, 0x3f, 0x86, 0x32, 0xd2, 0x27, 0xe1, 0xa7, 0x95, 0x55, 0xb3, 0x20, 0x52, - 0x75, 0xf7, 0x36, 0xb4, 0x92, 0x11, 0xa9, 0xbd, 0xbf, 0x9a, 0xf6, 0xec, 0xe7, 0x56, 0x1a, 0xb8, - 0xa4, 0xa7, 0x2d, 0xf9, 0xf8, 0x7f, 0x0d, 0xb8, 0xab, 0x37, 0xde, 0xb1, 0xf3, 0xf2, 0xd1, 0xe7, - 0xca, 0xfe, 0xbe, 0xb7, 0xa1, 0xf1, 0x7d, 0x2b, 0xda, 0xef, 0x3f, 0x8c, 0xbc, 0xee, 0xf1, 0x87, - 0x7d, 0xd6, 0xf4, 0x0c, 0x8a, 0xc1, 0xd8, 0xf5, 0x4e, 0x32, 0x11, 0xd9, 0x4c, 0x33, 0x8a, 0x42, - 0x2c, 0xae, 0xb0, 0xc5, 0xbb, 0x3a, 0xb1, 0xc5, 0x5b, 0xfc, 0x27, 0x50, 0xe1, 0x4e, 0x58, 0xce, - 0x57, 0x5a, 0xe9, 0xda, 0x64, 0x53, 0xa6, 0x63, 0xbd, 0x64, 0x8b, 0x6e, 0x45, 0xed, 0xf4, 0x2d, - 0xf9, 0xe4, 0xe4, 0xfa, 0xda, 0x8e, 0xd2, 0xe2, 0x43, 0x14, 0xde, 0x58, 0x8d, 0xf3, 0xb0, 0x1e, - 0x5c, 0x14, 0x47, 0x0c, 0x7c, 0x03, 0x8e, 0xf6, 0x4c, 0x58, 0x19, 0xaf, 0xd4, 0x0e, 0xbd, 0xb7, - 0xa1, 0x31, 0x63, 0x75, 0xdf, 0x6e, 0xc1, 0x76, 0x54, 0x25, 0xda, 0x59, 0xa5, 0xb0, 0x29, 0xb9, - 0x49, 0xc9, 0xbe, 0xbb, 0xb7, 0xa1, 0x6d, 0x19, 0x69, 0x10, 0xeb, 0xc2, 0x85, 0x28, 0x13, 0x72, - 0xb5, 0x8b, 0x9e, 0xa9, 0xae, 0x8c, 0xa2, 0xbc, 0x57, 0xef, 0x6d, 0x68, 0xdb, 0xc6, 0xca, 0x06, - 0xbe, 0x1f, 0xd5, 0x47, 0x56, 0x0e, 0xf9, 0x4a, 0xbc, 0xb9, 0xb6, 0x9b, 0x12, 0x4d, 0x35, 0xae, - 0x59, 0x02, 0x4a, 0xe2, 0x18, 0xae, 0x69, 0x70, 0x79, 0xbd, 0x84, 0x91, 0xb7, 0x99, 0x1c, 0xdf, - 0x66, 0x54, 0x79, 0x9b, 0x59, 0x7e, 0x20, 0x45, 0xda, 0x74, 0x7e, 0x08, 0xb5, 0x94, 0x88, 0x65, - 0x15, 0x28, 0x46, 0xcf, 0xbe, 0xd3, 0xe5, 0x98, 0xd6, 0xe0, 0xe0, 0x4b, 0x25, 0x83, 0xe0, 0x6e, - 0x7f, 0x38, 0x6a, 0xf6, 0x47, 0xca, 0x26, 0x4f, 0x1c, 0xf4, 0x9a, 0xad, 0x8e, 0x92, 0x55, 0xff, - 0x38, 0x0b, 0xe5, 0xf8, 0x94, 0xed, 0xbb, 0x7b, 0xc3, 0x62, 0x37, 0x53, 0x56, 0x76, 0x33, 0x2d, - 0x99, 0x7a, 0xfc, 0x0b, 0x0d, 0xfc, 0xa3, 0x59, 0x5b, 0x69, 0x83, 0x2a, 0x58, 0xbd, 0x37, 0x9f, - 0xff, 0x86, 0xf7, 0xe6, 0xe5, 0x08, 0xf4, 0x42, 0x3a, 0x02, 0x7d, 0xe9, 0xe9, 0xff, 0x22, 0x3d, - 0xca, 0x2d, 0x3f, 0xfd, 0x4f, 0x9f, 0x12, 0x7d, 0x62, 0x5b, 0xa7, 0x22, 0x64, 0x5b, 0xa4, 0xd2, - 0x3b, 0x34, 0xbc, 0x64, 0x87, 0xfe, 0x26, 0xd2, 0xfe, 0x01, 0x5c, 0x9c, 0x9e, 0xc4, 0x4f, 0x81, - 0x27, 0xce, 0x95, 0x2a, 0x55, 0x69, 0x2d, 0x8e, 0xbd, 0x11, 0x7f, 0xfb, 0xac, 0x26, 0xbb, 0x81, - 0xe2, 0xd1, 0x8a, 0x3f, 0x86, 0xf6, 0xff, 0x67, 0x00, 0x92, 0xf3, 0xa7, 0x5f, 0xd8, 0x95, 0x2b, - 0x79, 0xcb, 0xb2, 0x2f, 0xf0, 0x96, 0xbd, 0xec, 0x69, 0xb8, 0xaf, 0xa1, 0x1c, 0x9f, 0x38, 0x7e, - 0xf7, 0x89, 0xf5, 0xad, 0x8a, 0xfc, 0xcd, 0xc8, 0xad, 0x1d, 0x1f, 0xd9, 0xfd, 0xa2, 0x7d, 0x91, - 0x2a, 0x3e, 0xfb, 0x92, 0xe2, 0xcf, 0xb8, 0x6f, 0x39, 0x2e, 0xfc, 0x97, 0xbc, 0x9a, 0xe4, 0x89, - 0x9e, 0x4b, 0x4d, 0x74, 0x75, 0x21, 0x1c, 0xe4, 0xbf, 0x78, 0xd1, 0xdf, 0xaa, 0xc1, 0xff, 0x35, - 0x13, 0x79, 0x71, 0xe3, 0xd7, 0xdb, 0x9f, 0xab, 0xf4, 0xae, 0x77, 0x44, 0x7f, 0x9b, 0xe2, 0x5e, - 0xe8, 0xa3, 0xca, 0xbd, 0xc8, 0x47, 0xf5, 0x06, 0xe4, 0xf9, 0x76, 0x97, 0x7f, 0x9e, 0x7f, 0x8a, - 0xe3, 0x5f, 0xfa, 0x8d, 0x15, 0x55, 0x15, 0x4a, 0x3e, 0x6f, 0xef, 0xc5, 0x28, 0xdf, 0xe8, 0xfb, - 0x30, 0x74, 0x43, 0xe6, 0xff, 0xe2, 0x12, 0xf5, 0xbb, 0x76, 0xc9, 0x8b, 0xdd, 0x16, 0xea, 0xff, - 0xca, 0x40, 0x2d, 0x15, 0x41, 0xf0, 0x1d, 0x8a, 0x58, 0x2b, 0x97, 0xb3, 0xff, 0x07, 0xc9, 0xe5, - 0x54, 0x08, 0x6f, 0x29, 0x1d, 0xc2, 0x8b, 0xe2, 0xae, 0x9a, 0x32, 0x61, 0xd6, 0x19, 0x3b, 0x99, - 0xb5, 0xc6, 0xce, 0x8d, 0xf8, 0x23, 0x96, 0xdd, 0x36, 0x8f, 0x98, 0xad, 0x69, 0x12, 0x84, 0x7d, - 0x0a, 0x57, 0x85, 0x13, 0x81, 0xf7, 0x8f, 0x37, 0xd5, 0xe3, 0x4f, 0x5c, 0x0a, 0xa3, 0xfc, 0x32, - 0x27, 0xe0, 0x5f, 0xc8, 0x99, 0x36, 0x23, 0xac, 0xda, 0x85, 0x5a, 0x2a, 0x34, 0x43, 0xfa, 0xa4, - 0x6e, 0x46, 0xfe, 0xa4, 0x2e, 0xdb, 0x81, 0xfc, 0xe9, 0xb1, 0xe5, 0x5b, 0x6b, 0x5e, 0x66, 0xe6, - 0x08, 0xf5, 0xfb, 0x50, 0x95, 0xc3, 0xc4, 0xd8, 0xdb, 0x90, 0xb7, 0x43, 0x6b, 0x16, 0xb9, 0x47, - 0x2e, 0xaf, 0x46, 0x92, 0x75, 0x43, 0x6b, 0xa6, 0x71, 0x22, 0xf5, 0x0f, 0x32, 0xa0, 0x2c, 0xe3, - 0xa4, 0xef, 0xfe, 0x66, 0x9e, 0xf3, 0xdd, 0xdf, 0xcd, 0x54, 0x25, 0xd7, 0x7d, 0xba, 0x77, 0x27, - 0x52, 0x4a, 0xd6, 0x7c, 0x36, 0x96, 0x10, 0xec, 0x0e, 0x94, 0x7c, 0x8b, 0x3e, 0xaa, 0x6a, 0xae, - 0xb9, 0x39, 0x12, 0xe3, 0xd4, 0xdf, 0xcd, 0x40, 0x51, 0xc4, 0xb4, 0xad, 0xf5, 0x57, 0xbd, 0x09, - 0x45, 0xfe, 0x81, 0xd5, 0xe8, 0x85, 0xb8, 0x95, 0x30, 0xf3, 0x08, 0xcf, 0x6e, 0xf0, 0x48, 0xbf, - 0xb4, 0xff, 0xea, 0xc0, 0x31, 0x5c, 0x8d, 0xe0, 0xe2, 0xc3, 0x57, 0xc6, 0x4c, 0x3c, 0x29, 0xc0, - 0x9f, 0xf6, 0x02, 0x02, 0xd1, 0xeb, 0x01, 0xea, 0xaf, 0x42, 0x51, 0xc4, 0xcc, 0xad, 0xad, 0xca, - 0xcb, 0x3e, 0xae, 0xb9, 0x03, 0x90, 0x04, 0xd1, 0xad, 0xcb, 0x41, 0xbd, 0x07, 0xa5, 0x28, 0x6e, - 0x0e, 0xe7, 0x5f, 0x52, 0xb4, 0xb8, 0x92, 0x24, 0x57, 0xc6, 0x11, 0x5f, 0x3b, 0xe8, 0x79, 0x93, - 0x13, 0x72, 0x96, 0xbf, 0x0b, 0x74, 0x3f, 0x6b, 0xb4, 0xf2, 0x06, 0x5a, 0xfa, 0x73, 0x17, 0x31, - 0x11, 0xbb, 0x07, 0xb1, 0xbc, 0x7c, 0x99, 0x6b, 0x41, 0x6d, 0x46, 0x37, 0xf9, 0x68, 0x96, 0x3d, - 0x14, 0xde, 0xd4, 0x1e, 0xbd, 0xc1, 0x99, 0x91, 0x5f, 0x25, 0x4e, 0xd5, 0x49, 0x93, 0xc8, 0xd4, - 0x3a, 0x54, 0xe5, 0x60, 0x1f, 0xb5, 0x09, 0xdb, 0xfb, 0x56, 0x68, 0xa0, 0xfc, 0x89, 0x5e, 0x86, - 0xe2, 0xf3, 0x17, 0x7f, 0xa4, 0xe7, 0xef, 0x32, 0x9d, 0xc6, 0x89, 0xd4, 0x3f, 0xce, 0x81, 0xb2, - 0x8c, 0x7b, 0xd1, 0xad, 0xc6, 0x9b, 0x50, 0xf1, 0x68, 0x5e, 0xa4, 0x3e, 0x6d, 0xc6, 0x41, 0xd2, - 0x7d, 0x80, 0xd4, 0xf7, 0x6d, 0x4a, 0x76, 0xb0, 0xc7, 0xbf, 0x70, 0x73, 0x85, 0x5f, 0x61, 0x73, - 0xbc, 0x09, 0x4d, 0xeb, 0x2a, 0xdd, 0x58, 0xeb, 0x79, 0x13, 0xba, 0x2c, 0x29, 0xbc, 0x13, 0x3c, - 0x02, 0xb5, 0xaa, 0x95, 0x84, 0x4b, 0x82, 0xce, 0xef, 0xc4, 0x2d, 0x81, 0x30, 0x10, 0x77, 0x80, - 0x4b, 0x1c, 0x30, 0x0a, 0xa2, 0x37, 0xf5, 0x27, 0xe2, 0x3b, 0x5c, 0x59, 0x7a, 0x53, 0xbf, 0xe5, - 0xd2, 0x5d, 0x49, 0x7a, 0x69, 0x78, 0x22, 0xbe, 0x40, 0x28, 0xbe, 0x6a, 0x80, 0xa8, 0x5b, 0xfc, - 0x4b, 0x65, 0xbe, 0x15, 0x04, 0xfc, 0x29, 0xc6, 0xb2, 0x78, 0x83, 0x53, 0x00, 0xe3, 0x97, 0x6e, - 0xc5, 0x47, 0xe4, 0x90, 0x04, 0xc4, 0x83, 0x90, 0xfc, 0x13, 0x72, 0x48, 0x70, 0x15, 0x4a, 0x3f, - 0xf1, 0x5c, 0x8b, 0xbc, 0x1c, 0x15, 0xaa, 0x55, 0x11, 0xd3, 0xfb, 0xc6, 0x1c, 0x37, 0x02, 0x87, - 0xae, 0x9b, 0xf3, 0x5b, 0x82, 0x3c, 0xa1, 0xfe, 0xeb, 0x0c, 0x5c, 0x5c, 0xee, 0x6b, 0x9a, 0x46, - 0x55, 0x28, 0xb5, 0x06, 0x3d, 0xbd, 0xdf, 0xdc, 0xef, 0x28, 0x1b, 0x6c, 0x0b, 0x2a, 0x83, 0xdd, - 0xcf, 0x3a, 0xad, 0x11, 0x07, 0x64, 0xe8, 0x0d, 0x87, 0xa1, 0xbe, 0xd7, 0x6d, 0xb7, 0x3b, 0x7d, - 0x6e, 0x51, 0x0c, 0x76, 0x3f, 0xd3, 0x7b, 0x83, 0x16, 0xff, 0xd8, 0x54, 0x14, 0xf1, 0x30, 0x54, - 0x72, 0x14, 0x0f, 0x41, 0xc1, 0xf0, 0x98, 0xcc, 0xf3, 0x28, 0xef, 0xa7, 0x43, 0xbd, 0xd5, 0x1f, - 0x29, 0x05, 0x4c, 0xf5, 0x0f, 0x7b, 0x3d, 0x4a, 0x51, 0x38, 0x67, 0x6b, 0xb0, 0x7f, 0xa0, 0x75, - 0x86, 0x43, 0x7d, 0xd8, 0xfd, 0x71, 0x47, 0x29, 0x51, 0xc9, 0x5a, 0xf7, 0x71, 0xb7, 0xcf, 0x01, - 0x65, 0x56, 0x84, 0xec, 0x7e, 0xb7, 0xcf, 0xdf, 0xae, 0xd8, 0x6f, 0x7e, 0xa1, 0x54, 0xf0, 0xc7, - 0xf0, 0x70, 0x5f, 0xa9, 0xb2, 0x32, 0xe4, 0x7b, 0x9d, 0x27, 0x9d, 0x9e, 0x52, 0x53, 0xff, 0x43, - 0x36, 0xd2, 0x88, 0x29, 0xce, 0xe9, 0x9b, 0x68, 0x81, 0xeb, 0xce, 0x17, 0xe3, 0x4e, 0xcb, 0x4a, - 0x9d, 0xf6, 0x4d, 0xbe, 0x68, 0x7c, 0x0b, 0x6a, 0x71, 0x70, 0x80, 0xf4, 0x0e, 0x7e, 0x35, 0x02, - 0xae, 0x39, 0xbe, 0x28, 0xac, 0x39, 0xbe, 0x98, 0xdb, 0x21, 0x5a, 0xd9, 0x28, 0x73, 0xf9, 0x4c, - 0x2a, 0x23, 0x84, 0x7f, 0x4b, 0xfc, 0x15, 0xa0, 0x84, 0xbe, 0x70, 0xed, 0xe8, 0x7b, 0x96, 0x25, - 0x04, 0x1c, 0xba, 0x76, 0xb8, 0x1c, 0x9c, 0x50, 0x5e, 0x09, 0x4e, 0x90, 0x37, 0x67, 0x48, 0x6f, - 0xce, 0xe9, 0x0f, 0x3d, 0xf3, 0x0f, 0x59, 0x4a, 0x1f, 0x7a, 0x7e, 0x1b, 0xd8, 0x64, 0xe1, 0xd3, - 0xab, 0x74, 0x12, 0x59, 0x95, 0xc8, 0x14, 0x81, 0x89, 0x77, 0x45, 0xf6, 0x06, 0x6c, 0x2d, 0x51, - 0x93, 0x2d, 0x5d, 0xd6, 0xea, 0x69, 0x52, 0x76, 0x1f, 0x2e, 0x88, 0xb9, 0x9d, 0xea, 0x5b, 0x71, - 0xf5, 0x94, 0xa3, 0x9a, 0x49, 0x0f, 0xab, 0xbf, 0x02, 0xa5, 0x28, 0xa4, 0xed, 0xc5, 0xca, 0xee, - 0x9a, 0x71, 0x55, 0xff, 0xc1, 0x26, 0x94, 0xe3, 0x00, 0xb7, 0x6f, 0x34, 0x3b, 0xe8, 0x83, 0x1f, - 0xc1, 0x89, 0x2c, 0x62, 0x4a, 0x08, 0x88, 0x46, 0x4a, 0xdc, 0xd6, 0x5a, 0xf8, 0x76, 0xa4, 0xb1, - 0x71, 0xc8, 0xa1, 0x6f, 0xd3, 0xd3, 0x33, 0xb6, 0x2b, 0x5d, 0x12, 0x2d, 0x6b, 0x25, 0x04, 0xd0, - 0x42, 0xbb, 0x0a, 0xf4, 0x9b, 0x38, 0xa3, 0x6f, 0x5f, 0xdb, 0xee, 0x09, 0xf2, 0x3d, 0xe7, 0xdb, - 0xd7, 0xf4, 0xb9, 0x12, 0x1e, 0x5d, 0xc3, 0x63, 0x0a, 0xa2, 0x0f, 0xf4, 0xbd, 0x02, 0xe5, 0x45, - 0xfc, 0x85, 0x47, 0x31, 0x23, 0x16, 0xd1, 0xf7, 0x1d, 0xd3, 0xa3, 0x5a, 0x5e, 0x1e, 0xd5, 0xe5, - 0x39, 0x0d, 0x2b, 0x73, 0x5a, 0x0d, 0xa1, 0x28, 0x82, 0xfc, 0x5e, 0xdc, 0xe1, 0x2f, 0xec, 0x2a, - 0x05, 0xb2, 0x86, 0x13, 0xdd, 0x4c, 0xc5, 0x9f, 0x4b, 0x15, 0xcb, 0x2d, 0x55, 0x4c, 0xfd, 0x5b, - 0x9b, 0x00, 0x49, 0xb0, 0x20, 0x7b, 0x67, 0x29, 0x30, 0x39, 0xb3, 0xb2, 0xed, 0x2f, 0xc5, 0x23, - 0x2f, 0xbd, 0xc5, 0xb4, 0xf9, 0x0d, 0xde, 0x62, 0x7a, 0x00, 0xb5, 0xc0, 0x9f, 0xbc, 0xd4, 0xe1, - 0x5e, 0x09, 0xfc, 0x49, 0xec, 0x6f, 0x7f, 0x17, 0x30, 0x49, 0xef, 0x34, 0x26, 0x86, 0xea, 0x8a, - 0xd6, 0x52, 0x0e, 0xfc, 0xc9, 0x60, 0xfc, 0x55, 0x9b, 0x5f, 0x91, 0x33, 0x83, 0x50, 0x5f, 0x27, - 0x25, 0xb6, 0xcc, 0x20, 0x6c, 0xcb, 0x82, 0xe2, 0x36, 0xd4, 0x91, 0x76, 0x45, 0x58, 0x54, 0xcd, - 0x20, 0x39, 0x60, 0x51, 0x7f, 0x27, 0x3a, 0x92, 0x5e, 0xf2, 0xe5, 0xb2, 0x8f, 0x84, 0x21, 0x2e, - 0x29, 0x11, 0x8d, 0x75, 0xae, 0x5f, 0xfe, 0x72, 0x54, 0x4c, 0xba, 0xfa, 0x5d, 0xbf, 0xcd, 0x6f, - 0xfa, 0x5d, 0xbf, 0x1d, 0x80, 0xe4, 0xa5, 0x4c, 0x5c, 0x81, 0xf1, 0x65, 0x9d, 0x32, 0xbf, 0x86, - 0x73, 0xef, 0x35, 0xa8, 0xca, 0x1f, 0x02, 0xa6, 0x4b, 0x38, 0x9e, 0x6b, 0xf1, 0x6f, 0xa7, 0xf4, - 0x7e, 0xf2, 0x81, 0x92, 0xb9, 0xa7, 0x42, 0x45, 0xfa, 0x72, 0x11, 0x52, 0xec, 0x19, 0xc1, 0xb1, - 0xf8, 0x8e, 0x86, 0xe1, 0x1e, 0x59, 0x4a, 0xe6, 0xde, 0x1d, 0x54, 0xba, 0xe5, 0xef, 0x06, 0x01, - 0x14, 0xfa, 0x9e, 0x3f, 0x33, 0x1c, 0x41, 0x67, 0x2d, 0x02, 0xa4, 0x7b, 0x17, 0x2e, 0xad, 0xfd, - 0x0a, 0x12, 0x5d, 0xe1, 0xb2, 0x67, 0x73, 0xc7, 0xe2, 0x97, 0x91, 0xf6, 0xce, 0xc7, 0xbe, 0x6d, - 0x2a, 0x99, 0x7b, 0x9f, 0x2c, 0x7d, 0x25, 0xe3, 0xb0, 0xbf, 0x3b, 0x38, 0xec, 0xb7, 0x3b, 0x6d, - 0x7e, 0x4d, 0xa8, 0xdb, 0x6f, 0xf5, 0x0e, 0x87, 0xdd, 0x27, 0x62, 0x2f, 0xec, 0x7c, 0x11, 0x25, - 0x37, 0xef, 0xed, 0x45, 0xaf, 0x31, 0x44, 0xb5, 0xee, 0x0d, 0x9a, 0x6d, 0xbe, 0x87, 0xc6, 0xef, - 0x31, 0x8d, 0x76, 0xf9, 0xd7, 0x35, 0xb4, 0xce, 0xf0, 0xb0, 0x37, 0x8a, 0xde, 0x7e, 0xaa, 0x03, - 0x74, 0x5b, 0x9d, 0xdd, 0x8e, 0xf6, 0x18, 0x09, 0xb2, 0xf7, 0x7e, 0x08, 0x8d, 0xe7, 0xdd, 0xef, - 0xc1, 0xb6, 0xb5, 0xf6, 0x9a, 0x74, 0x87, 0x0a, 0xf7, 0xd0, 0x81, 0xce, 0x53, 0xe4, 0xe9, 0xd3, - 0x3a, 0xbd, 0x0e, 0x05, 0xc1, 0xde, 0xfb, 0x69, 0x46, 0xd2, 0x27, 0xa3, 0x3b, 0x1a, 0x31, 0x40, - 0x74, 0xb8, 0x0c, 0xd2, 0x2c, 0xc3, 0x54, 0x32, 0xec, 0x32, 0xb0, 0x14, 0xa8, 0xe7, 0x4d, 0x0c, - 0x47, 0xd9, 0xa4, 0x70, 0xd7, 0x08, 0x4e, 0x77, 0xf9, 0x94, 0x2c, 0x7b, 0x15, 0xae, 0xc6, 0xb0, - 0x9e, 0x77, 0x7a, 0xe0, 0xdb, 0x9e, 0x6f, 0x87, 0xe7, 0x1c, 0x9d, 0xbb, 0xf7, 0x7f, 0x8b, 0xc3, - 0xda, 0xd4, 0x2c, 0xc3, 0x02, 0x9a, 0xa6, 0x99, 0xc0, 0x48, 0xb0, 0x29, 0x1b, 0xec, 0x0a, 0x5c, - 0x20, 0xa9, 0xbe, 0x84, 0xc8, 0xb0, 0x57, 0xe0, 0x4a, 0x64, 0xf4, 0x2e, 0x23, 0x37, 0x11, 0xa9, - 0x59, 0x14, 0x2a, 0xb9, 0x82, 0xcc, 0xee, 0xfe, 0xe0, 0x4f, 0x7e, 0x7e, 0x23, 0xf3, 0x67, 0x3f, - 0xbf, 0x91, 0xf9, 0x8f, 0x3f, 0xbf, 0xb1, 0xf1, 0x07, 0xff, 0xf9, 0x46, 0xe6, 0xc7, 0xef, 0x1c, - 0xd9, 0xe1, 0xf1, 0x62, 0x7c, 0x7f, 0xe2, 0xcd, 0xde, 0x9d, 0x19, 0xa1, 0x6f, 0x9f, 0xf1, 0xed, - 0x25, 0x4a, 0xb8, 0xd6, 0xbb, 0xf3, 0x93, 0xa3, 0x77, 0xe7, 0xe3, 0x77, 0x71, 0xa2, 0x8f, 0x0b, - 0x73, 0xdf, 0x0b, 0xbd, 0x87, 0xff, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x55, 0x1d, 0xeb, 0xdc, - 0x89, 0x00, 0x00, + 0x56, 0xab, 0xd4, 0x92, 0x5a, 0x52, 0xb7, 0x9e, 0xb3, 0xb3, 0x3b, 0xc3, 0x22, 0x59, 0x5d, 0x94, + 0x58, 0x64, 0x4d, 0x92, 0xd5, 0x2d, 0xcd, 0xde, 0x45, 0x22, 0x8b, 0x99, 0xac, 0x4a, 0x55, 0x32, + 0x93, 0xca, 0x4c, 0x76, 0x55, 0x0d, 0xb0, 0xb8, 0x83, 0xfb, 0xb1, 0x8f, 0x8b, 0xfb, 0x77, 0xe1, + 0xf5, 0xd7, 0xc2, 0x6b, 0x03, 0x86, 0x01, 0xc3, 0x86, 0x61, 0x63, 0x80, 0xf5, 0xfe, 0x79, 0xe1, + 0x9f, 0x35, 0x6c, 0xaf, 0x0d, 0x1b, 0xf6, 0x62, 0x6d, 0x60, 0x6d, 0x8f, 0xbf, 0x0d, 0x1b, 0xb0, + 0xff, 0xfc, 0x61, 0xe3, 0x9c, 0x88, 0xcc, 0x8c, 0x24, 0xd9, 0xdd, 0x92, 0x66, 0x16, 0xf0, 0x4f, + 0x15, 0xe3, 0x3c, 0xe2, 0x1d, 0x27, 0xce, 0x39, 0x71, 0x22, 0x12, 0x60, 0xe6, 0x18, 0xee, 0x83, + 0x99, 0xef, 0x85, 0x1e, 0xcb, 0xe1, 0xef, 0x1b, 0xef, 0x1c, 0xdb, 0xe1, 0xc9, 0xfc, 0xe8, 0xc1, + 0xd8, 0x9b, 0xbe, 0x7b, 0xec, 0x1d, 0x7b, 0xef, 0x12, 0xf2, 0x68, 0x3e, 0xa1, 0x14, 0x25, 0xe8, + 0x17, 0x67, 0xba, 0x01, 0x8e, 0x37, 0x3e, 0x15, 0xbf, 0x37, 0x42, 0x7b, 0x6a, 0x05, 0xa1, 0x31, + 0x9d, 0x71, 0x80, 0xfa, 0x87, 0x19, 0xc8, 0x8d, 0x2e, 0x66, 0x16, 0xab, 0xc3, 0xba, 0x6d, 0x36, + 0x32, 0x5b, 0x99, 0xed, 0xbc, 0xb6, 0x6e, 0x9b, 0x6c, 0x0b, 0x2a, 0xae, 0x17, 0xf6, 0xe7, 0x8e, + 0x63, 0x1c, 0x39, 0x56, 0x63, 0x7d, 0x2b, 0xb3, 0x5d, 0xd2, 0x64, 0x10, 0x7b, 0x05, 0xca, 0xc6, + 0x3c, 0xf4, 0x74, 0xdb, 0x1d, 0xfb, 0x8d, 0x2c, 0xe1, 0x4b, 0x08, 0xe8, 0xba, 0x63, 0x9f, 0x5d, + 0x86, 0xfc, 0x99, 0x6d, 0x86, 0x27, 0x8d, 0x1c, 0xe5, 0xc8, 0x13, 0x08, 0x0d, 0xc6, 0x86, 0x63, + 0x35, 0xf2, 0x1c, 0x4a, 0x09, 0x84, 0x86, 0x54, 0x48, 0x61, 0x2b, 0xb3, 0x5d, 0xd6, 0x78, 0x82, + 0xdd, 0x02, 0xb0, 0xdc, 0xf9, 0xf4, 0x99, 0xe1, 0xcc, 0xad, 0xa0, 0x51, 0x24, 0x94, 0x04, 0x51, + 0x7f, 0x00, 0xe5, 0x69, 0x70, 0xbc, 0x67, 0x19, 0xa6, 0xe5, 0xb3, 0x6b, 0x50, 0x9c, 0x06, 0xc7, + 0x7a, 0x68, 0x1c, 0x8b, 0x26, 0x14, 0xa6, 0xc1, 0xf1, 0xc8, 0x38, 0x66, 0xd7, 0xa1, 0x44, 0x88, + 0x8b, 0x19, 0x6f, 0x43, 0x5e, 0x43, 0x42, 0x6c, 0xb1, 0xfa, 0xbb, 0x05, 0x28, 0xf6, 0xec, 0xd0, + 0xf2, 0x0d, 0x87, 0x5d, 0x85, 0x82, 0x1d, 0xb8, 0x73, 0xc7, 0x21, 0xf6, 0x92, 0x26, 0x52, 0xec, + 0x2a, 0xe4, 0xed, 0x4f, 0x9e, 0x19, 0x0e, 0xe7, 0xdd, 0x5b, 0xd3, 0x78, 0x92, 0x35, 0xa0, 0x60, + 0xbf, 0xff, 0x11, 0x22, 0xb2, 0x02, 0x21, 0xd2, 0x84, 0x79, 0xf4, 0x10, 0x31, 0xb9, 0x18, 0x43, + 0x69, 0xc2, 0x7c, 0xf4, 0x01, 0x62, 0xb0, 0xf5, 0x59, 0xc2, 0x50, 0x1a, 0x4b, 0x99, 0x53, 0x29, + 0xd8, 0x01, 0x35, 0x2c, 0x65, 0x1e, 0x95, 0x32, 0xe7, 0xa5, 0x14, 0x05, 0x42, 0xa4, 0x09, 0xc3, + 0x4b, 0x29, 0xc5, 0x98, 0xb8, 0x94, 0x39, 0x2f, 0xa5, 0xbc, 0x95, 0xd9, 0xce, 0x11, 0x86, 0x97, + 0x72, 0x19, 0x72, 0x26, 0xc2, 0x61, 0x2b, 0xb3, 0x9d, 0xd9, 0x5b, 0xd3, 0x28, 0x85, 0xd0, 0x00, + 0xa1, 0x15, 0xec, 0x60, 0x84, 0x06, 0x02, 0x7a, 0x84, 0xd0, 0x2a, 0xf6, 0x06, 0x42, 0x8f, 0x04, + 0x74, 0x82, 0xd0, 0xda, 0x56, 0x66, 0x7b, 0x1d, 0xa1, 0x98, 0x62, 0x37, 0xa0, 0x68, 0x1a, 0xa1, + 0x85, 0x88, 0xba, 0x68, 0x72, 0x04, 0x40, 0x1c, 0xce, 0x38, 0xc4, 0x6d, 0x88, 0x46, 0x47, 0x00, + 0xa6, 0x42, 0x05, 0xc9, 0x22, 0xbc, 0x22, 0xf0, 0x32, 0x90, 0x7d, 0x08, 0x55, 0xd3, 0x1a, 0xdb, + 0x53, 0xc3, 0xe1, 0x6d, 0xda, 0xdc, 0xca, 0x6c, 0x57, 0x1e, 0x6e, 0x3c, 0xa0, 0x35, 0x11, 0x63, + 0xf6, 0xd6, 0xb4, 0x14, 0x19, 0xfb, 0x04, 0x6a, 0x22, 0xfd, 0xfe, 0x43, 0xea, 0x58, 0x46, 0x7c, + 0x4a, 0x8a, 0xef, 0xfd, 0x87, 0x9f, 0xec, 0xad, 0x69, 0x69, 0x42, 0x76, 0x17, 0xaa, 0xf1, 0x12, + 0x41, 0xc6, 0x4b, 0xa2, 0x56, 0x29, 0x28, 0x36, 0xeb, 0xab, 0xc0, 0x73, 0x91, 0xe0, 0xb2, 0xe8, + 0xb7, 0x08, 0xc0, 0xb6, 0x00, 0x4c, 0x6b, 0x62, 0xcc, 0x9d, 0x10, 0xd1, 0x57, 0x44, 0x07, 0x4a, + 0x30, 0x76, 0x0b, 0xca, 0xf3, 0x19, 0xb6, 0xf2, 0x89, 0xe1, 0x34, 0xae, 0x0a, 0x82, 0x04, 0x84, + 0xb9, 0xe3, 0x3c, 0x47, 0xec, 0x35, 0x31, 0xba, 0x11, 0x00, 0x87, 0xf7, 0x99, 0x35, 0x46, 0x54, + 0x43, 0x14, 0x2c, 0xd2, 0xb8, 0x8a, 0xec, 0x60, 0xc7, 0x76, 0x1b, 0xd7, 0x69, 0x06, 0xf3, 0x04, + 0xbb, 0x09, 0xd9, 0xc0, 0x1f, 0x37, 0x6e, 0x50, 0xfb, 0x81, 0xb7, 0xbf, 0x73, 0x3e, 0xf3, 0x35, + 0x04, 0xef, 0x14, 0x21, 0x4f, 0xab, 0x49, 0xbd, 0x09, 0xa5, 0x03, 0xc3, 0x37, 0xa6, 0x9a, 0x35, + 0x61, 0x0a, 0x64, 0x67, 0x5e, 0x20, 0xd6, 0x11, 0xfe, 0x54, 0x7b, 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, 0x1f, 0x3b, 0xde, 0x91, 0x58, 0x23, 0x25, 0x4d, 0xa4, 0xd4, + 0xff, 0x27, 0x03, 0x85, 0x96, 0xe7, 0x60, 0x76, 0xd7, 0xa0, 0xe8, 0x5b, 0x8e, 0x9e, 0x14, 0x57, + 0xf0, 0x2d, 0xe7, 0xc0, 0x0b, 0x10, 0x31, 0xf6, 0x38, 0x82, 0xaf, 0xda, 0xc2, 0xd8, 0x23, 0x44, + 0x54, 0x81, 0xac, 0x54, 0x81, 0xeb, 0x50, 0x0a, 0x8f, 0x1c, 0x9d, 0xe0, 0x39, 0x82, 0x17, 0xc3, + 0x23, 0xa7, 0x8f, 0xa8, 0x6b, 0x50, 0x34, 0x8f, 0x38, 0x26, 0x4f, 0x98, 0x82, 0x79, 0x84, 0x08, + 0xf5, 0x53, 0x28, 0x6b, 0xc6, 0x99, 0xa8, 0xc6, 0x15, 0x28, 0x60, 0x06, 0x42, 0xfe, 0xe5, 0xb4, + 0x7c, 0x78, 0xe4, 0x74, 0x4d, 0x04, 0x63, 0x25, 0x6c, 0x93, 0xea, 0x90, 0xd3, 0xf2, 0x63, 0xcf, + 0xe9, 0x9a, 0xea, 0x08, 0xa0, 0xe5, 0xf9, 0xfe, 0x77, 0x6e, 0xc2, 0x65, 0xc8, 0x9b, 0xd6, 0x2c, + 0x3c, 0xe1, 0xa2, 0x43, 0xe3, 0x09, 0xf5, 0x3e, 0x94, 0x70, 0x5c, 0x7a, 0x76, 0x10, 0xb2, 0x5b, + 0x90, 0x73, 0xec, 0x20, 0x6c, 0x64, 0xb6, 0xb2, 0x0b, 0xa3, 0x46, 0x70, 0x75, 0x0b, 0x4a, 0xfb, + 0xc6, 0xf9, 0x13, 0x1c, 0x39, 0xcc, 0x8d, 0x86, 0x50, 0x0c, 0x89, 0x18, 0xcf, 0x2a, 0xc0, 0xc8, + 0xf0, 0x8f, 0xad, 0x90, 0x24, 0xdd, 0x7f, 0xcf, 0x40, 0x65, 0x38, 0x3f, 0xfa, 0x7a, 0x6e, 0xf9, + 0x17, 0x58, 0xe7, 0x6d, 0xc8, 0x86, 0x17, 0x33, 0xe2, 0xa8, 0x3f, 0xbc, 0xca, 0xb3, 0x97, 0xf0, + 0x0f, 0x90, 0x49, 0x43, 0x12, 0x6c, 0x84, 0xeb, 0x99, 0x56, 0xd4, 0x07, 0x79, 0xad, 0x80, 0xc9, + 0xae, 0x89, 0xdb, 0x85, 0x37, 0x13, 0xa3, 0xb0, 0xee, 0xcd, 0xd8, 0x16, 0xe4, 0xc7, 0x27, 0xb6, + 0x63, 0xd2, 0x00, 0xa4, 0xeb, 0xcc, 0x11, 0x38, 0x4a, 0xbe, 0x77, 0xa6, 0x07, 0xf6, 0x4f, 0x22, + 0xf1, 0x5f, 0xf4, 0xbd, 0xb3, 0xa1, 0xfd, 0x13, 0x4b, 0x1d, 0x89, 0x3d, 0x08, 0xa0, 0x30, 0x6c, + 0x35, 0x7b, 0x4d, 0x4d, 0x59, 0xc3, 0xdf, 0x9d, 0x2f, 0xba, 0xc3, 0xd1, 0x50, 0xc9, 0xb0, 0x3a, + 0x40, 0x7f, 0x30, 0xd2, 0x45, 0x7a, 0x9d, 0x15, 0x60, 0xbd, 0xdb, 0x57, 0xb2, 0x48, 0x83, 0xf0, + 0x6e, 0x5f, 0xc9, 0xb1, 0x22, 0x64, 0x9b, 0xfd, 0x2f, 0x95, 0x3c, 0xfd, 0xe8, 0xf5, 0x94, 0x82, + 0xfa, 0xa7, 0xeb, 0x50, 0x1e, 0x1c, 0x7d, 0x65, 0x8d, 0x43, 0x6c, 0x33, 0xce, 0x52, 0xcb, 0x7f, + 0x66, 0xf9, 0xd4, 0xec, 0xac, 0x26, 0x52, 0xd8, 0x10, 0xf3, 0x88, 0x1a, 0x97, 0xd5, 0xd6, 0xcd, + 0x23, 0xa2, 0x1b, 0x9f, 0x58, 0x53, 0x83, 0x1a, 0x87, 0x74, 0x94, 0xc2, 0x55, 0xe1, 0x1d, 0x7d, + 0x45, 0xcd, 0xcb, 0x6a, 0xf8, 0x93, 0xdd, 0x86, 0x0a, 0xcf, 0x43, 0x9e, 0x5f, 0xc0, 0x41, 0x8b, + 0x93, 0xaf, 0x20, 0x4f, 0x3e, 0xe2, 0xa4, 0x5c, 0x39, 0x52, 0xec, 0x6d, 0x1c, 0xd4, 0x17, 0x33, + 0xda, 0x3b, 0xfa, 0x8a, 0x63, 0x4b, 0x7c, 0x46, 0x7b, 0x47, 0x5f, 0x11, 0xea, 0x2d, 0xd8, 0x0c, + 0xe6, 0x47, 0xc1, 0xd8, 0xb7, 0x67, 0xa1, 0xed, 0xb9, 0x9c, 0xa6, 0x4c, 0x34, 0x8a, 0x8c, 0x20, + 0xe2, 0x6d, 0x28, 0xcd, 0xe6, 0x47, 0xba, 0xed, 0x4e, 0x3c, 0x12, 0xfb, 0x95, 0x87, 0x35, 0x3e, + 0x30, 0x07, 0xf3, 0xa3, 0xae, 0x3b, 0xf1, 0xb4, 0xe2, 0x8c, 0xff, 0x60, 0x2a, 0xd4, 0x5c, 0x2f, + 0xd4, 0x51, 0x55, 0xd0, 0xa7, 0x56, 0x68, 0xd0, 0x7e, 0xc0, 0x37, 0xfc, 0x9e, 0x37, 0x3e, 0xdd, + 0xb7, 0x42, 0x43, 0xbd, 0x07, 0x45, 0xc1, 0x87, 0x7b, 0x7f, 0x68, 0xb9, 0x86, 0x1b, 0xea, 0xb1, + 0xd2, 0x50, 0xe2, 0x80, 0xae, 0xa9, 0xfe, 0x2c, 0x03, 0xca, 0x50, 0xaa, 0x0a, 0x32, 0xaf, 0x94, + 0x1c, 0xaf, 0x02, 0x18, 0xe3, 0xb1, 0x37, 0xe7, 0xd9, 0xf0, 0x09, 0x56, 0x16, 0x90, 0xae, 0x29, + 0xf7, 0x5f, 0x36, 0xd5, 0x7f, 0xaf, 0x41, 0x35, 0xe2, 0x93, 0x16, 0x7d, 0x45, 0xc0, 0xa2, 0x1e, + 0x0c, 0xe6, 0xa9, 0x95, 0x5f, 0x0c, 0xe6, 0x9c, 0xfb, 0x2a, 0x14, 0x48, 0xc3, 0x08, 0xa2, 0x51, + 0xe1, 0x29, 0xf5, 0xcf, 0x32, 0x50, 0xeb, 0xba, 0xa6, 0x75, 0x3e, 0x1c, 0x1b, 0x6e, 0xd4, 0x29, + 0x76, 0xa0, 0xdb, 0x08, 0xd3, 0x83, 0xb1, 0xe1, 0x0a, 0xe5, 0xa0, 0x62, 0x07, 0x31, 0x1d, 0xb6, + 0x81, 0x13, 0x50, 0x51, 0xeb, 0x94, 0x63, 0x99, 0x20, 0x54, 0xd8, 0x3d, 0xd8, 0x38, 0xb2, 0x1c, + 0xcf, 0x3d, 0xd6, 0x43, 0x4f, 0xe7, 0x5a, 0x0e, 0x6f, 0x4b, 0x8d, 0x83, 0x47, 0xde, 0x88, 0xb4, + 0x9d, 0xcb, 0x90, 0x9f, 0x19, 0x7e, 0x18, 0x34, 0x72, 0x5b, 0x59, 0x5c, 0xc6, 0x94, 0xc0, 0x6e, + 0xb6, 0x03, 0x7d, 0xee, 0xda, 0x5f, 0xcf, 0x79, 0x33, 0x4a, 0x5a, 0xc9, 0x0e, 0x0e, 0x29, 0xcd, + 0xb6, 0x41, 0xe1, 0x25, 0x53, 0xb6, 0xf2, 0x3c, 0xab, 0x13, 0x9c, 0x32, 0x26, 0x61, 0xf7, 0xff, + 0xae, 0x43, 0x69, 0x77, 0xee, 0x8e, 0x71, 0x30, 0xd8, 0x1d, 0xc8, 0x4d, 0xe6, 0xee, 0x98, 0xda, + 0x12, 0x6f, 0xa5, 0xf1, 0x3a, 0xd1, 0x08, 0x89, 0x12, 0xc8, 0xf0, 0x8f, 0x51, 0x72, 0x2d, 0x49, + 0x20, 0x84, 0xab, 0xff, 0x30, 0xc3, 0x73, 0xdc, 0x75, 0x8c, 0x63, 0x56, 0x82, 0x5c, 0x7f, 0xd0, + 0xef, 0x28, 0x6b, 0xac, 0x0a, 0xa5, 0x6e, 0x7f, 0xd4, 0xd1, 0xfa, 0xcd, 0x9e, 0x92, 0xa1, 0xe5, + 0x3c, 0x6a, 0xee, 0xf4, 0x3a, 0xca, 0x3a, 0x62, 0x9e, 0x0c, 0x7a, 0xcd, 0x51, 0xb7, 0xd7, 0x51, + 0x72, 0x1c, 0xa3, 0x75, 0x5b, 0x23, 0xa5, 0xc4, 0x14, 0xa8, 0x1e, 0x68, 0x83, 0xf6, 0x61, 0xab, + 0xa3, 0xf7, 0x0f, 0x7b, 0x3d, 0x45, 0x61, 0x97, 0x60, 0x23, 0x86, 0x0c, 0x38, 0x70, 0x0b, 0x59, + 0x9e, 0x34, 0xb5, 0xa6, 0xf6, 0x58, 0xf9, 0x21, 0x2b, 0x41, 0xb6, 0xf9, 0xf8, 0xb1, 0xf2, 0x53, + 0x94, 0x0c, 0xe5, 0xa7, 0xdd, 0xbe, 0xfe, 0xa4, 0xd9, 0x3b, 0xec, 0x28, 0x3f, 0x5d, 0x8f, 0xd2, + 0x03, 0xad, 0xdd, 0xd1, 0x94, 0x9f, 0xe6, 0xd8, 0x26, 0x54, 0x7f, 0x3c, 0xe8, 0x77, 0xf6, 0x9b, + 0x07, 0x07, 0x54, 0x91, 0x9f, 0x96, 0xd4, 0xff, 0x92, 0x83, 0x1c, 0xb6, 0x84, 0xa9, 0x89, 0x14, + 0x8c, 0x9b, 0x88, 0x62, 0x68, 0x27, 0xf7, 0x27, 0x7f, 0x71, 0x7b, 0x8d, 0xcb, 0xbf, 0xd7, 0x20, + 0xeb, 0xd8, 0x21, 0x0d, 0x6b, 0xbc, 0x76, 0x84, 0xce, 0xb8, 0xb7, 0xa6, 0x21, 0x8e, 0xdd, 0x82, + 0x0c, 0x17, 0x84, 0x95, 0x87, 0x75, 0xb1, 0xb8, 0xc4, 0x4e, 0xba, 0xb7, 0xa6, 0x65, 0x66, 0xec, + 0x26, 0x64, 0x9e, 0x09, 0xa9, 0x58, 0xe5, 0x78, 0xbe, 0x97, 0x22, 0xf6, 0x19, 0xdb, 0x82, 0xec, + 0xd8, 0xe3, 0x1a, 0x61, 0x8c, 0xe7, 0x3b, 0x0b, 0xe6, 0x3f, 0xf6, 0x1c, 0x76, 0x07, 0xb2, 0xbe, + 0x71, 0x46, 0x23, 0x1b, 0x0f, 0x57, 0xbc, 0x75, 0x21, 0x91, 0x6f, 0x9c, 0x61, 0x25, 0x26, 0x24, + 0x47, 0xe2, 0x4a, 0x44, 0xe3, 0x8d, 0xc5, 0x4c, 0xd8, 0x16, 0x64, 0xce, 0x48, 0x92, 0xc4, 0x4a, + 0xd0, 0x53, 0xdb, 0x35, 0xbd, 0xb3, 0xe1, 0xcc, 0x1a, 0x23, 0xc5, 0x19, 0x7b, 0x1d, 0xb2, 0xc1, + 0xfc, 0x88, 0x24, 0x49, 0xe5, 0xe1, 0xe6, 0xd2, 0x9e, 0x80, 0x05, 0x05, 0xf3, 0x23, 0x76, 0x0f, + 0x72, 0x63, 0xcf, 0xf7, 0x85, 0x34, 0x51, 0xa2, 0x0a, 0x47, 0xdb, 0x21, 0x2a, 0x85, 0x88, 0xc7, + 0x02, 0x43, 0x92, 0x21, 0x31, 0x51, 0xb2, 0x1f, 0x61, 0x81, 0x21, 0xbb, 0x2b, 0x36, 0xb9, 0xaa, + 0x5c, 0xeb, 0x68, 0x0b, 0xc4, 0x7c, 0x10, 0x8b, 0x83, 0x34, 0x35, 0xce, 0x49, 0xe3, 0x8c, 0x89, + 0xa2, 0xbd, 0x0f, 0xeb, 0x34, 0x35, 0xce, 0xd9, 0x5d, 0xc8, 0x3e, 0xb3, 0xc6, 0xa4, 0x7c, 0xc6, + 0xa5, 0x89, 0x41, 0x7a, 0x42, 0xcd, 0x43, 0x34, 0xcd, 0x7b, 0xcf, 0x31, 0x49, 0x0f, 0x8d, 0xc7, + 0x72, 0xd7, 0x73, 0xcc, 0x27, 0x34, 0x96, 0x84, 0xc4, 0x2d, 0xdf, 0x98, 0x9f, 0xa3, 0x34, 0x52, + 0xf8, 0xe6, 0x6c, 0xcc, 0xcf, 0xbb, 0x26, 0x0a, 0x7f, 0xd7, 0x7c, 0x46, 0xda, 0x67, 0x46, 0xc3, + 0x9f, 0x68, 0x1e, 0x05, 0x96, 0x63, 0x8d, 0x43, 0xfb, 0x99, 0x1d, 0x5e, 0x90, 0x7e, 0x99, 0xd1, + 0x64, 0xd0, 0x4e, 0x01, 0x72, 0xd6, 0xf9, 0xcc, 0x57, 0xf7, 0xa0, 0x28, 0x4a, 0x59, 0xb2, 0xb1, + 0xae, 0x43, 0xc9, 0x0e, 0xf4, 0xb1, 0xe7, 0x06, 0xa1, 0xd0, 0x9d, 0x8a, 0x76, 0xd0, 0xc2, 0x24, + 0x8a, 0x4b, 0xd3, 0x08, 0xf9, 0x26, 0x54, 0xd5, 0xe8, 0xb7, 0xfa, 0x10, 0x20, 0x69, 0x16, 0xd6, + 0xc9, 0xb1, 0xdc, 0x48, 0x4d, 0x73, 0x2c, 0x37, 0xe6, 0x59, 0x97, 0x78, 0xae, 0x43, 0x39, 0xd6, + 0x8c, 0x59, 0x15, 0x32, 0x86, 0xd8, 0xfe, 0x32, 0x86, 0xba, 0x8d, 0x8a, 0x6a, 0xa4, 0xfb, 0xa6, + 0x71, 0x98, 0x8a, 0x36, 0xc5, 0xcc, 0x91, 0xfa, 0x7d, 0xa8, 0x6a, 0x56, 0x30, 0x77, 0xc2, 0x96, + 0xe7, 0xb4, 0xad, 0x09, 0x7b, 0x1b, 0x20, 0x4e, 0x07, 0x42, 0x4b, 0x49, 0xe6, 0x6e, 0xdb, 0x9a, + 0x68, 0x12, 0x5e, 0xfd, 0x4f, 0x39, 0xd2, 0xf7, 0xda, 0x5c, 0xd1, 0x12, 0x1a, 0x55, 0x46, 0xd2, + 0xa8, 0xe2, 0xbd, 0x61, 0x3d, 0xad, 0x55, 0x9e, 0xd8, 0xa6, 0x69, 0xb9, 0x91, 0xf6, 0xc8, 0x53, + 0x38, 0xd8, 0x86, 0x73, 0x4c, 0x0b, 0xaa, 0xfe, 0x90, 0x45, 0x85, 0x4e, 0x67, 0xbe, 0x15, 0x04, + 0x5c, 0x6f, 0x31, 0x9c, 0xe3, 0x68, 0x6d, 0xe7, 0x5f, 0xb4, 0xb6, 0xaf, 0x43, 0x09, 0xb7, 0x3c, + 0xb2, 0xfa, 0x0a, 0xbc, 0xf7, 0x85, 0x79, 0xcb, 0xde, 0x80, 0xa2, 0xd0, 0xd7, 0xc5, 0xa2, 0x12, + 0xd3, 0xa5, 0xcd, 0x81, 0x5a, 0x84, 0x65, 0x0d, 0x54, 0xf2, 0xa6, 0x53, 0xcb, 0x0d, 0xa3, 0x7d, + 0x5a, 0x24, 0xd9, 0x5b, 0x50, 0xf6, 0x5c, 0x9d, 0x2b, 0xf5, 0x62, 0x55, 0x89, 0xe9, 0x3b, 0x70, + 0x0f, 0x09, 0xaa, 0x95, 0x3c, 0xf1, 0x0b, 0xab, 0xe2, 0x78, 0x67, 0xfa, 0xd8, 0xf0, 0x4d, 0x5a, + 0x59, 0x25, 0xad, 0xe8, 0x78, 0x67, 0x2d, 0xc3, 0x37, 0xb9, 0xde, 0xf2, 0xb5, 0x3b, 0x9f, 0xd2, + 0x6a, 0xaa, 0x69, 0x22, 0xc5, 0x6e, 0x42, 0x79, 0xec, 0xcc, 0x83, 0xd0, 0xf2, 0x77, 0x2e, 0xb8, + 0x99, 0xa6, 0x25, 0x00, 0xac, 0xd7, 0xcc, 0xb7, 0xa7, 0x86, 0x7f, 0x41, 0x4b, 0xa7, 0xa4, 0x45, + 0x49, 0xda, 0x68, 0x4e, 0x6d, 0xf3, 0x9c, 0xdb, 0x6a, 0x1a, 0x4f, 0x20, 0xfd, 0x09, 0x59, 0xd2, + 0x01, 0xad, 0x8f, 0x92, 0x16, 0x25, 0x69, 0x1c, 0xe8, 0x27, 0xad, 0x88, 0xb2, 0x26, 0x52, 0x29, + 0xa5, 0x7b, 0xf3, 0xb9, 0x4a, 0x37, 0x5b, 0xd4, 0x7b, 0x3c, 0xdf, 0x3e, 0xb6, 0x85, 0xd6, 0x72, + 0x89, 0xeb, 0x3d, 0x1c, 0x44, 0x04, 0x1f, 0x43, 0xed, 0xd8, 0x72, 0x2d, 0xdf, 0x08, 0x2d, 0x53, + 0x47, 0xb9, 0x78, 0x99, 0x3a, 0x4e, 0x0c, 0xf3, 0xe3, 0x08, 0x85, 0xb2, 0xa6, 0x7a, 0x2c, 0xa5, + 0xd4, 0xaf, 0xa1, 0x28, 0xc6, 0x06, 0xb7, 0x2e, 0x5c, 0x77, 0x69, 0xb9, 0xce, 0xb7, 0x2e, 0x84, + 0xb3, 0x3b, 0x50, 0x13, 0x95, 0x08, 0x42, 0xdf, 0x76, 0x8f, 0xc5, 0xac, 0xab, 0x72, 0xe0, 0x90, + 0x60, 0xa8, 0x61, 0xe0, 0xbc, 0xd0, 0x8d, 0x23, 0xdb, 0xc1, 0xf5, 0x9d, 0x15, 0xda, 0xd0, 0xdc, + 0x71, 0x9a, 0x1c, 0xa4, 0x0e, 0xa0, 0x14, 0x8d, 0xe4, 0x2f, 0xa5, 0x4c, 0x75, 0x06, 0x55, 0xb9, + 0x85, 0xbf, 0x9c, 0x86, 0x70, 0x0d, 0x22, 0x08, 0x3d, 0xdf, 0x32, 0x23, 0x27, 0x8d, 0x1d, 0x0c, + 0x29, 0xad, 0xfe, 0x56, 0x06, 0x2a, 0xa4, 0xc9, 0x0c, 0x48, 0x4f, 0x63, 0x6f, 0x03, 0x1b, 0xfb, + 0x96, 0x11, 0x5a, 0xba, 0x75, 0x1e, 0xfa, 0x86, 0xd0, 0x57, 0xb8, 0xd2, 0xa3, 0x70, 0x4c, 0x07, + 0x11, 0x5c, 0x65, 0xb9, 0x0d, 0x95, 0x99, 0xe1, 0x07, 0x91, 0xfe, 0xcb, 0x4b, 0x07, 0x0e, 0x12, + 0xda, 0xa7, 0xe2, 0x1e, 0xfb, 0xc6, 0x54, 0x0f, 0xbd, 0x53, 0xcb, 0xe5, 0x9a, 0x3f, 0xb7, 0x79, + 0xea, 0x04, 0x1f, 0x21, 0x98, 0x0c, 0x80, 0x7f, 0x97, 0x81, 0xda, 0x01, 0x9f, 0xa0, 0x9f, 0x5b, + 0x17, 0x6d, 0x6e, 0x68, 0x8e, 0x23, 0xe1, 0x92, 0xd3, 0xe8, 0x37, 0xbb, 0x05, 0x95, 0xd9, 0xa9, + 0x75, 0xa1, 0xa7, 0x8c, 0xb2, 0x32, 0x82, 0x5a, 0x24, 0x46, 0xde, 0x84, 0x82, 0x47, 0x0d, 0x11, + 0xdb, 0xb1, 0xd8, 0xc5, 0xa4, 0x16, 0x6a, 0x82, 0x00, 0x35, 0xbb, 0x38, 0x2b, 0x59, 0x85, 0x14, + 0x99, 0x51, 0xf5, 0x2f, 0x43, 0x1e, 0x51, 0x41, 0x23, 0xcf, 0x55, 0x32, 0x4a, 0xb0, 0xf7, 0xa0, + 0x36, 0xf6, 0xa6, 0x33, 0x3d, 0x62, 0x17, 0x1b, 0x73, 0x5a, 0xfc, 0x55, 0x90, 0xe4, 0x80, 0xe7, + 0xa5, 0xfe, 0x5e, 0x16, 0x4a, 0x54, 0x07, 0x21, 0x01, 0x6d, 0xf3, 0x3c, 0x92, 0x80, 0x65, 0x2d, + 0x6f, 0x9b, 0xb8, 0xc1, 0xbc, 0x44, 0x8b, 0x8c, 0xb5, 0xc3, 0xac, 0xac, 0x1d, 0x5e, 0x85, 0x82, + 0x50, 0x0d, 0x73, 0x5c, 0x44, 0xce, 0x9f, 0xaf, 0x18, 0xe6, 0x57, 0x29, 0x86, 0x38, 0x84, 0x9c, + 0xc6, 0x3a, 0xc7, 0xad, 0x98, 0x4b, 0x41, 0x20, 0x50, 0x07, 0x21, 0xb2, 0x7c, 0x2b, 0xa6, 0xe5, + 0x5b, 0x03, 0x8a, 0xcf, 0xec, 0xc0, 0xc6, 0x09, 0x52, 0xe2, 0x12, 0x43, 0x24, 0xa5, 0x61, 0x28, + 0xbf, 0x6c, 0x18, 0xe2, 0x66, 0x1b, 0xce, 0x31, 0xb7, 0x50, 0xa2, 0x66, 0x37, 0x9d, 0x63, 0x8f, + 0xbd, 0x0f, 0x57, 0x12, 0xb4, 0x68, 0x0d, 0x79, 0xf2, 0xc8, 0x59, 0xa5, 0xb1, 0x98, 0x92, 0x5a, + 0x44, 0x26, 0xe4, 0x7d, 0xd8, 0x94, 0x58, 0x66, 0xa8, 0x89, 0x05, 0x24, 0x1e, 0xcb, 0xda, 0x46, + 0x4c, 0x4e, 0x0a, 0x5a, 0xa0, 0xfe, 0x93, 0x75, 0xa8, 0xed, 0x7a, 0xbe, 0x65, 0x1f, 0xbb, 0xc9, + 0xac, 0x5b, 0x32, 0x52, 0xa2, 0x99, 0xb8, 0x2e, 0xcd, 0xc4, 0xdb, 0x50, 0x99, 0x70, 0x46, 0x3d, + 0x3c, 0xe2, 0xfe, 0x8d, 0x9c, 0x06, 0x02, 0x34, 0x3a, 0x72, 0x50, 0x7e, 0x44, 0x04, 0xc4, 0x9c, + 0x23, 0xe6, 0x88, 0x09, 0xb7, 0x45, 0xf6, 0x3d, 0xda, 0x20, 0x4c, 0xcb, 0xb1, 0x42, 0x3e, 0x3c, + 0xf5, 0x87, 0xaf, 0x46, 0x4a, 0x89, 0x54, 0xa7, 0x07, 0x9a, 0x35, 0x69, 0x92, 0x26, 0x87, 0xfb, + 0x45, 0x9b, 0xc8, 0x05, 0xaf, 0xd8, 0x5c, 0x0a, 0xdf, 0x90, 0x97, 0xcb, 0x2a, 0x75, 0x04, 0xe5, + 0x18, 0x8c, 0x6a, 0xb9, 0xd6, 0x11, 0xaa, 0xf8, 0x1a, 0xab, 0x40, 0xb1, 0xd5, 0x1c, 0xb6, 0x9a, + 0xed, 0x8e, 0x92, 0x41, 0xd4, 0xb0, 0x33, 0xe2, 0xea, 0xf7, 0x3a, 0xdb, 0x80, 0x0a, 0xa6, 0xda, + 0x9d, 0xdd, 0xe6, 0x61, 0x6f, 0xa4, 0x64, 0x59, 0x0d, 0xca, 0xfd, 0x81, 0xde, 0x6c, 0x8d, 0xba, + 0x83, 0xbe, 0x92, 0x53, 0x7f, 0x08, 0xa5, 0xd6, 0x89, 0x35, 0x3e, 0x7d, 0x5e, 0x2f, 0x92, 0x7f, + 0xc0, 0x1a, 0x9f, 0x0a, 0x55, 0x7a, 0xc1, 0x3f, 0x60, 0x8d, 0x4f, 0xd5, 0x0e, 0x94, 0x0f, 0x0c, + 0x3f, 0xb4, 0xa9, 0x5e, 0x9f, 0x40, 0x2d, 0x4e, 0xb4, 0xad, 0x49, 0xa4, 0x64, 0xb0, 0x58, 0xc1, + 0x8e, 0x51, 0x5a, 0x9a, 0x50, 0x7d, 0x1b, 0xaa, 0x32, 0x80, 0xdd, 0x84, 0xac, 0x69, 0x4d, 0x56, + 0x08, 0x51, 0x04, 0xab, 0x4f, 0xa0, 0xda, 0x8a, 0x36, 0xcd, 0xe7, 0x55, 0xfd, 0x21, 0xd4, 0x69, + 0xc5, 0x8f, 0x8f, 0xa2, 0x25, 0xbf, 0xbe, 0x62, 0xc9, 0x57, 0x91, 0xa6, 0x75, 0x24, 0xd6, 0xfc, + 0x87, 0x50, 0x39, 0xf0, 0xbd, 0x99, 0xe5, 0x87, 0x94, 0xad, 0x02, 0xd9, 0x53, 0xeb, 0x42, 0xe4, + 0x8a, 0x3f, 0x13, 0xb7, 0xcd, 0xba, 0xec, 0xb6, 0x79, 0x08, 0xa5, 0x88, 0xed, 0x1b, 0xf3, 0xfc, + 0x00, 0x45, 0x27, 0xf1, 0xd8, 0x56, 0x80, 0x85, 0x3d, 0x00, 0x98, 0xc5, 0x00, 0xd1, 0x71, 0x91, + 0x65, 0x22, 0x32, 0xd7, 0x24, 0x0a, 0xf5, 0x55, 0x28, 0x3e, 0xb1, 0xad, 0x33, 0xd1, 0xfc, 0x67, + 0xb6, 0x75, 0x16, 0x35, 0x1f, 0x7f, 0xab, 0xff, 0xa2, 0x0c, 0x25, 0x5a, 0x5f, 0xed, 0xe7, 0x7b, + 0xca, 0xbe, 0x8d, 0x02, 0xb7, 0x25, 0xd6, 0x53, 0x6e, 0x85, 0xda, 0xc8, 0x57, 0xd7, 0xab, 0x00, + 0xd2, 0x5a, 0xe7, 0x92, 0xab, 0x1c, 0xc6, 0x4b, 0x1c, 0x35, 0x1f, 0xda, 0x8b, 0x82, 0xaf, 0x1d, + 0x61, 0xf0, 0x26, 0x00, 0xf6, 0x80, 0xeb, 0x25, 0x64, 0xe2, 0x72, 0xdd, 0xed, 0x52, 0x64, 0x7f, + 0x1c, 0x39, 0x56, 0x64, 0x15, 0x91, 0xb2, 0x82, 0x09, 0x92, 0x63, 0x96, 0x1f, 0xa0, 0xb8, 0x22, + 0x57, 0xba, 0x16, 0x25, 0xd9, 0x1b, 0x90, 0x43, 0x21, 0x2f, 0xac, 0x98, 0x4b, 0x51, 0x0f, 0x4a, + 0xbb, 0x94, 0x46, 0x04, 0x6c, 0x1b, 0x8a, 0x24, 0x5a, 0x2c, 0x94, 0x34, 0x52, 0x6f, 0x47, 0x42, + 0x5f, 0x8b, 0xd0, 0xec, 0x4d, 0xc8, 0x4f, 0x4e, 0xad, 0x8b, 0xa0, 0x51, 0x23, 0xba, 0x4b, 0x2b, + 0xd6, 0xac, 0xc6, 0x29, 0xd8, 0x5d, 0xa8, 0xfb, 0xd6, 0x44, 0x27, 0xdf, 0x19, 0x0a, 0x99, 0xa0, + 0x51, 0x27, 0x19, 0x52, 0xf5, 0xad, 0x49, 0x0b, 0x81, 0xa3, 0x23, 0x27, 0x60, 0xf7, 0xa0, 0x40, + 0xab, 0x07, 0xd5, 0x36, 0xa9, 0xe4, 0x68, 0x29, 0x6a, 0x02, 0xcb, 0xde, 0x07, 0x10, 0xca, 0xa1, + 0x7e, 0x74, 0x41, 0x3e, 0xe7, 0x78, 0x31, 0xc9, 0xf3, 0x5f, 0x56, 0x21, 0xdf, 0x80, 0x3c, 0x4e, + 0x92, 0xa0, 0x71, 0x8d, 0x72, 0xde, 0x4c, 0xcf, 0x20, 0xaa, 0x29, 0xe1, 0xd9, 0x36, 0x94, 0x70, + 0xa2, 0xe8, 0x38, 0x1c, 0x0d, 0x59, 0x5b, 0x16, 0xb3, 0x0a, 0x77, 0x06, 0xeb, 0x6c, 0xf8, 0xb5, + 0xc3, 0xee, 0x43, 0xce, 0xc4, 0xc5, 0x7c, 0x9d, 0x72, 0xbc, 0x2a, 0x8d, 0x0b, 0x0a, 0xab, 0xb6, + 0x35, 0x21, 0x05, 0x9e, 0x68, 0xd8, 0x1e, 0xd4, 0x71, 0x1a, 0x3d, 0xa4, 0xcd, 0x1e, 0xbb, 0xaf, + 0x71, 0x83, 0xb8, 0x5e, 0x5b, 0xe0, 0xea, 0x0b, 0x22, 0xea, 0xec, 0x8e, 0x1b, 0xfa, 0x17, 0x5a, + 0xcd, 0x95, 0x61, 0xec, 0x06, 0x5a, 0x59, 0x3d, 0x6f, 0x7c, 0x6a, 0x99, 0x8d, 0x57, 0x22, 0x0d, + 0x88, 0xa7, 0xd9, 0xa7, 0x50, 0xa3, 0x89, 0x85, 0x49, 0x2c, 0xbc, 0x71, 0x93, 0x84, 0xa9, 0x3c, + 0x65, 0x22, 0x94, 0x96, 0xa6, 0x44, 0x11, 0x6f, 0x07, 0x7a, 0x68, 0x4d, 0x67, 0x9e, 0x8f, 0x7a, + 0xf6, 0xab, 0x91, 0x6f, 0x68, 0x14, 0x81, 0x70, 0x23, 0x8e, 0x4f, 0xc8, 0x74, 0x6f, 0x32, 0x09, + 0xac, 0xb0, 0x71, 0x8b, 0xd6, 0x4d, 0x3d, 0x3a, 0x28, 0x1b, 0x10, 0x94, 0x36, 0xc2, 0x40, 0x37, + 0x2f, 0x5c, 0x63, 0x6a, 0x8f, 0x1b, 0xb7, 0xb9, 0x3a, 0x6f, 0x07, 0x6d, 0x0e, 0x90, 0x35, 0xea, + 0xad, 0x94, 0x46, 0x7d, 0x09, 0xf2, 0xe6, 0x11, 0x2e, 0xc7, 0xd7, 0x28, 0xdb, 0x9c, 0x79, 0xd4, + 0x35, 0xd9, 0x3b, 0x50, 0x9e, 0x45, 0x22, 0xb0, 0xa1, 0xca, 0x7e, 0x83, 0x58, 0x32, 0x6a, 0x09, + 0x05, 0x9a, 0xb2, 0xbb, 0x96, 0x11, 0xce, 0x7d, 0x6b, 0xd7, 0x31, 0x8e, 0x1b, 0x77, 0x28, 0x27, + 0x19, 0x84, 0xb5, 0x73, 0xbc, 0x63, 0x7b, 0x6c, 0xd0, 0xca, 0xbf, 0xcb, 0xf5, 0x2e, 0x01, 0xe9, + 0x9a, 0x89, 0x22, 0x6a, 0x08, 0x65, 0xea, 0x75, 0x59, 0x11, 0x35, 0x48, 0x9b, 0xba, 0xf1, 0x98, + 0x34, 0x74, 0xea, 0xb9, 0x0f, 0x17, 0x04, 0x54, 0x6a, 0x79, 0x49, 0x92, 0x6c, 0x6f, 0x4d, 0x96, + 0x53, 0x3b, 0x79, 0x92, 0xe4, 0x37, 0x7e, 0x08, 0x6c, 0x79, 0xcc, 0x5f, 0x26, 0x2d, 0xf3, 0x42, + 0x5a, 0x7e, 0x6f, 0xfd, 0x93, 0x8c, 0xfa, 0x04, 0x6a, 0x29, 0x61, 0xb0, 0x52, 0xea, 0x73, 0x95, + 0xcb, 0x98, 0x0a, 0x6b, 0x9a, 0x27, 0x22, 0x75, 0xda, 0x76, 0x8f, 0x85, 0x23, 0x8f, 0xab, 0xd3, + 0x94, 0x56, 0xff, 0x34, 0x0b, 0xd5, 0x3d, 0x23, 0x38, 0xd9, 0x37, 0x66, 0xc3, 0xd0, 0x08, 0x03, + 0x9c, 0x22, 0x27, 0x46, 0x70, 0x32, 0x35, 0x66, 0x5c, 0xf9, 0xcd, 0x70, 0x2f, 0x81, 0x80, 0xa1, + 0xe6, 0x8b, 0x93, 0x13, 0x93, 0x03, 0xf7, 0xe0, 0x73, 0xe1, 0x02, 0x88, 0xd3, 0x28, 0x9a, 0x82, + 0x93, 0xf9, 0x64, 0x12, 0x17, 0x15, 0x25, 0xd9, 0x5d, 0xa8, 0x89, 0x9f, 0xa4, 0xf9, 0x9e, 0x8b, + 0x53, 0xd6, 0x34, 0x90, 0x3d, 0x82, 0x8a, 0x00, 0x8c, 0x22, 0x41, 0x5a, 0x8f, 0x5d, 0x3b, 0x09, + 0x42, 0x93, 0xa9, 0xd8, 0x8f, 0xe0, 0x8a, 0x94, 0xdc, 0xf5, 0xfc, 0xfd, 0xb9, 0x13, 0xda, 0xad, + 0xbe, 0x50, 0x33, 0x5e, 0x59, 0x62, 0x4f, 0x48, 0xb4, 0xd5, 0x9c, 0xe9, 0xda, 0xee, 0xdb, 0x2e, + 0xc9, 0xe5, 0xac, 0x96, 0x06, 0x2e, 0x50, 0x19, 0xe7, 0x24, 0x8e, 0xd3, 0x54, 0xc6, 0x39, 0x2e, + 0x58, 0x01, 0xd8, 0xb7, 0xc2, 0x13, 0xcf, 0x24, 0x1d, 0x33, 0x5e, 0xb0, 0x43, 0x19, 0xa5, 0xa5, + 0x29, 0xb1, 0x3b, 0xd1, 0x7e, 0x1b, 0xbb, 0x21, 0x69, 0x9a, 0x59, 0x2d, 0x4a, 0xe2, 0x56, 0xe5, + 0x1b, 0xee, 0xb1, 0x15, 0x34, 0x2a, 0x5b, 0xd9, 0xed, 0x8c, 0x26, 0x52, 0xea, 0xdf, 0x5c, 0x87, + 0x3c, 0x1f, 0xc9, 0x57, 0xa0, 0x7c, 0x44, 0xbe, 0x71, 0x34, 0xc4, 0x85, 0xbf, 0x9b, 0x00, 0xfd, + 0xf9, 0x94, 0x6b, 0x88, 0xc2, 0x85, 0x93, 0xd1, 0xe8, 0x37, 0x66, 0xe9, 0xcd, 0x43, 0x2c, 0x2b, + 0x4b, 0x50, 0x91, 0xc2, 0x4a, 0xf8, 0xde, 0x19, 0xcd, 0x86, 0x1c, 0x21, 0xa2, 0x24, 0xb9, 0xd4, + 0x69, 0xd7, 0x43, 0xa6, 0x3c, 0xe1, 0x4a, 0x04, 0x68, 0xb9, 0xe1, 0xa2, 0xbb, 0xa9, 0xb0, 0xe4, + 0x6e, 0x62, 0xb7, 0x00, 0xf5, 0xcf, 0xb1, 0x35, 0x70, 0xad, 0x56, 0x9f, 0x7a, 0xb8, 0xa4, 0x49, + 0x10, 0x5c, 0x20, 0xa6, 0x37, 0xa3, 0x4e, 0xcd, 0x6b, 0xf8, 0x93, 0x7d, 0x14, 0xcf, 0x4e, 0x6a, + 0xa3, 0xd0, 0xd6, 0xc5, 0xae, 0x20, 0xcf, 0x63, 0x2d, 0x45, 0x87, 0x39, 0xa1, 0xa8, 0xe7, 0xda, + 0x3a, 0xfe, 0x54, 0x3b, 0x00, 0x9a, 0x77, 0x16, 0x58, 0x21, 0xf9, 0x55, 0xaf, 0x51, 0x13, 0x53, + 0x27, 0x62, 0xde, 0xd9, 0x81, 0x17, 0xc4, 0x06, 0xed, 0xfa, 0x6a, 0x83, 0x56, 0x7d, 0x17, 0x8a, + 0xa8, 0x07, 0x18, 0xa1, 0xc1, 0xee, 0x0a, 0x57, 0x16, 0xd7, 0x5e, 0x84, 0x4f, 0x2f, 0x29, 0x43, + 0x38, 0xb7, 0x7a, 0x51, 0xb9, 0xc4, 0xf3, 0x9a, 0x64, 0x32, 0xc6, 0x7b, 0x90, 0xc8, 0x50, 0x68, + 0x16, 0xaf, 0x40, 0x19, 0xab, 0x46, 0xc7, 0x04, 0x42, 0x2e, 0x94, 0x7c, 0xef, 0xac, 0x85, 0x69, + 0xf5, 0xdf, 0x67, 0xa0, 0x32, 0xf0, 0x4d, 0xdc, 0xfc, 0x86, 0x33, 0x6b, 0xfc, 0x52, 0xfb, 0x1b, + 0xf5, 0x10, 0xcf, 0x71, 0x0c, 0x12, 0xb3, 0xc2, 0x64, 0x8b, 0x01, 0xec, 0x7d, 0xc8, 0x4d, 0x50, + 0x9c, 0x66, 0x65, 0xed, 0x5c, 0xca, 0x3e, 0xfa, 0x8d, 0x02, 0x56, 0x23, 0x52, 0xf5, 0xd7, 0xe3, + 0xf2, 0x49, 0xea, 0xca, 0xce, 0xf4, 0x35, 0x3a, 0xd6, 0x1a, 0xb6, 0x94, 0x0c, 0x2b, 0x41, 0xae, + 0xdd, 0x19, 0xb6, 0xb8, 0x4e, 0x8e, 0xda, 0xf9, 0x50, 0xdf, 0xed, 0x6a, 0xc3, 0x91, 0x92, 0xa3, + 0x73, 0x32, 0x02, 0xf4, 0x9a, 0xc3, 0x91, 0x52, 0x62, 0x00, 0x85, 0xc3, 0x7e, 0xf7, 0x47, 0x87, + 0x1d, 0x45, 0x51, 0xff, 0x75, 0x06, 0x20, 0xf1, 0xf9, 0xb2, 0xb7, 0xa0, 0x72, 0x46, 0x29, 0x5d, + 0x3a, 0x0c, 0x90, 0xdb, 0x08, 0x1c, 0x4d, 0x3a, 0xd2, 0x3b, 0x50, 0x8d, 0xb7, 0x0b, 0xd4, 0x1f, + 0x96, 0x4f, 0x05, 0x2a, 0x31, 0x7e, 0xe7, 0x82, 0xbd, 0x0d, 0x25, 0x0f, 0xdb, 0x81, 0xa4, 0x59, + 0x59, 0x79, 0x90, 0x9a, 0xaf, 0x15, 0x3d, 0x9e, 0x40, 0x3d, 0x63, 0xe2, 0x47, 0x26, 0x78, 0x4c, + 0xba, 0x8b, 0xa0, 0x96, 0x63, 0xcc, 0x03, 0x4b, 0xe3, 0xf8, 0x58, 0x4a, 0xe7, 0x13, 0x29, 0xad, + 0xfe, 0x18, 0xea, 0x43, 0x63, 0x3a, 0xe3, 0xb2, 0x9c, 0x1a, 0xc6, 0x20, 0x87, 0x73, 0x42, 0x4c, + 0x3d, 0xfa, 0x8d, 0x8b, 0xee, 0xc0, 0xf2, 0xc7, 0x96, 0x1b, 0xad, 0xd1, 0x28, 0x89, 0xe2, 0xf7, + 0x10, 0xa5, 0xb9, 0xe6, 0x9d, 0x45, 0xe2, 0x3c, 0x4a, 0xab, 0x7f, 0x27, 0x03, 0x15, 0xa9, 0x1a, + 0xec, 0x5d, 0xc8, 0x91, 0x42, 0x9a, 0x91, 0x05, 0xa1, 0x44, 0xc0, 0x7f, 0x73, 0x15, 0x06, 0x09, + 0xd9, 0x3d, 0xc8, 0x07, 0xa1, 0xe1, 0x47, 0xc7, 0x07, 0x8a, 0xc4, 0xb1, 0xe3, 0xcd, 0x5d, 0x53, + 0xe3, 0x68, 0xa6, 0x42, 0xd6, 0x72, 0x4d, 0xe1, 0xb4, 0x58, 0xa6, 0x42, 0xa4, 0xba, 0x05, 0xe5, + 0x38, 0x7b, 0x9c, 0x02, 0xda, 0xe0, 0xe9, 0x50, 0x59, 0x63, 0x65, 0xc8, 0x6b, 0xcd, 0xfe, 0xe3, + 0x8e, 0x92, 0x51, 0x7f, 0x96, 0x01, 0x48, 0xb8, 0xd8, 0x83, 0x54, 0x6d, 0x6f, 0x2c, 0xe6, 0xfa, + 0x80, 0xfe, 0x4a, 0x95, 0xbd, 0x09, 0xe5, 0xb9, 0x4b, 0x40, 0xcb, 0x14, 0x3b, 0x51, 0x02, 0x40, + 0x2b, 0x2a, 0x0a, 0x76, 0x59, 0xb0, 0xa2, 0x9e, 0x19, 0x8e, 0xfa, 0x3d, 0x28, 0xc7, 0xd9, 0xa1, + 0x61, 0xb8, 0x3b, 0xe8, 0xf5, 0x06, 0x4f, 0xbb, 0xfd, 0xc7, 0xca, 0x1a, 0x26, 0x0f, 0xb4, 0x4e, + 0xab, 0xd3, 0xc6, 0x64, 0x06, 0xe7, 0x6c, 0xeb, 0x50, 0xd3, 0x3a, 0xfd, 0x91, 0xae, 0x0d, 0x9e, + 0x2a, 0xeb, 0xea, 0x6f, 0x67, 0xa0, 0x36, 0x70, 0xcc, 0x96, 0xe7, 0xb4, 0x8c, 0x19, 0x6a, 0x1c, + 0xec, 0x07, 0xb0, 0x79, 0x34, 0x47, 0xad, 0x77, 0xe6, 0x18, 0x63, 0xeb, 0xc4, 0x73, 0x4c, 0x2b, + 0x5a, 0x84, 0xa9, 0x03, 0x12, 0xe1, 0xcb, 0x55, 0x88, 0xf8, 0x20, 0xa1, 0x65, 0x1f, 0x42, 0x75, + 0xe6, 0x7b, 0x47, 0x96, 0x1e, 0x78, 0x73, 0x7f, 0x6c, 0x2d, 0x99, 0x6b, 0x09, 0x6f, 0x85, 0xe8, + 0x86, 0x44, 0xa6, 0xfe, 0xbd, 0x75, 0xa8, 0xb6, 0x2d, 0x73, 0x3e, 0xfb, 0xcc, 0xb3, 0xdd, 0x56, + 0x78, 0xce, 0x3e, 0x80, 0xaa, 0xe7, 0x90, 0x1f, 0x52, 0x97, 0x8e, 0xe3, 0x57, 0xe5, 0x03, 0x1e, + 0xb5, 0x80, 0x0e, 0xef, 0xdf, 0x81, 0x4b, 0xdc, 0x30, 0x17, 0x7e, 0xaa, 0x73, 0xce, 0x8c, 0x6b, + 0x26, 0xaf, 0x29, 0x1c, 0xc5, 0x37, 0x68, 0x22, 0xff, 0x15, 0xb8, 0x2c, 0x91, 0xa3, 0x60, 0xe1, + 0xf4, 0xd9, 0xa5, 0x35, 0xb6, 0x19, 0xf3, 0xc6, 0x81, 0x02, 0x9f, 0xc1, 0xe5, 0xa8, 0x86, 0x63, + 0xde, 0x7b, 0x9c, 0x39, 0x27, 0x9b, 0x17, 0xa9, 0xde, 0x15, 0x15, 0xde, 0xf4, 0x64, 0x20, 0xe5, + 0xf5, 0x3e, 0x5c, 0x31, 0xb1, 0xf5, 0x3a, 0xef, 0xfc, 0x53, 0xcb, 0x9a, 0xe9, 0x8e, 0x11, 0x84, + 0xe2, 0xdc, 0x91, 0x11, 0x72, 0x07, 0x71, 0x9f, 0x5b, 0xd6, 0xac, 0x67, 0x04, 0xa1, 0xfa, 0xd7, + 0xb2, 0x50, 0xe6, 0x5e, 0x05, 0xec, 0xae, 0x6d, 0x28, 0x7a, 0x47, 0x5f, 0xe9, 0x7e, 0x6c, 0x6d, + 0x2f, 0x9d, 0x2d, 0x16, 0xbc, 0xa3, 0xaf, 0x34, 0x6b, 0xc2, 0xde, 0x8a, 0xb6, 0x3a, 0xb4, 0xcc, + 0xd7, 0x65, 0xdf, 0x78, 0xa4, 0xd6, 0x8b, 0xad, 0x0f, 0x4d, 0xce, 0x47, 0x50, 0xb1, 0xdd, 0xc0, + 0xf2, 0x43, 0xee, 0x49, 0x29, 0x3e, 0x7f, 0x10, 0x38, 0x19, 0x39, 0x57, 0x1e, 0x41, 0x85, 0x7b, + 0x56, 0x38, 0x53, 0xe9, 0xf9, 0x4c, 0x9c, 0x8c, 0x98, 0x3e, 0x85, 0x7a, 0x22, 0xe6, 0x88, 0xaf, + 0xfc, 0x5c, 0xbe, 0x5a, 0x4c, 0x49, 0xac, 0x0f, 0xe1, 0x6a, 0x70, 0x6a, 0xcf, 0x74, 0x51, 0x53, + 0xcf, 0xa5, 0x63, 0x05, 0x7d, 0x76, 0x2a, 0xdc, 0xf9, 0x0c, 0xb1, 0x5d, 0x42, 0x0e, 0xdc, 0xfe, + 0xdc, 0x71, 0x0e, 0x4e, 0xd9, 0x9b, 0xb0, 0x29, 0xc8, 0x67, 0xa7, 0xd1, 0x5c, 0x21, 0x63, 0x33, + 0xaf, 0xd5, 0x39, 0xe2, 0xe0, 0x54, 0x68, 0x72, 0x1f, 0x43, 0x83, 0x1f, 0x77, 0x8b, 0x46, 0x19, + 0x93, 0x89, 0x35, 0x0e, 0x75, 0x54, 0x1d, 0x84, 0xef, 0xff, 0x0a, 0xe1, 0xb9, 0x7b, 0xa8, 0x49, + 0x58, 0x94, 0x7c, 0xea, 0xdf, 0xcf, 0x40, 0x99, 0x17, 0x8b, 0x23, 0xf4, 0x1a, 0x64, 0x5f, 0x30, + 0x3a, 0x88, 0x63, 0xf7, 0x61, 0xd3, 0x30, 0x4d, 0x51, 0x80, 0x65, 0xf2, 0x22, 0xb8, 0x38, 0xd8, + 0x30, 0x4c, 0xb3, 0x29, 0xe0, 0x24, 0x56, 0xb7, 0x41, 0xb1, 0x03, 0x3d, 0xb2, 0x2b, 0x93, 0xc3, + 0xed, 0x92, 0x56, 0xb7, 0x03, 0x61, 0x56, 0x72, 0x57, 0x71, 0x6a, 0xc0, 0x73, 0x2f, 0x1e, 0x70, + 0x95, 0x01, 0x68, 0x16, 0xad, 0xfd, 0x56, 0x78, 0xfe, 0x59, 0xae, 0x94, 0x51, 0x40, 0xfd, 0xa7, + 0x15, 0xa8, 0x34, 0x5d, 0xc3, 0xb9, 0xf8, 0x89, 0x45, 0x27, 0xf3, 0xe4, 0x38, 0x9c, 0xcd, 0x45, + 0x17, 0xf0, 0x63, 0xab, 0x32, 0x41, 0xa8, 0x66, 0xb7, 0xa1, 0xe2, 0xcd, 0xc3, 0x18, 0xcf, 0x0f, + 0xb2, 0x80, 0x83, 0x88, 0x20, 0xe6, 0x8f, 0x9d, 0xd2, 0x11, 0x3f, 0x69, 0xe5, 0x09, 0x7f, 0xac, + 0xa9, 0xc5, 0xfc, 0x44, 0x70, 0x07, 0x6a, 0xa1, 0x3d, 0xb5, 0xe8, 0xec, 0x6e, 0x3e, 0xb5, 0x4c, + 0x1e, 0xd2, 0xc7, 0xa3, 0xc4, 0x5a, 0x02, 0x86, 0xb9, 0x4c, 0xad, 0xa9, 0xe7, 0x5f, 0xf0, 0x5c, + 0x0a, 0x3c, 0x17, 0x0e, 0xa2, 0x5c, 0xde, 0x06, 0x76, 0x66, 0xd8, 0xa1, 0x9e, 0xce, 0x8a, 0x6b, + 0xc7, 0x0a, 0x62, 0x46, 0x72, 0x76, 0x57, 0xa1, 0x60, 0xda, 0xc1, 0x69, 0x77, 0x20, 0x34, 0x63, + 0x91, 0xc2, 0xb6, 0x04, 0x63, 0x03, 0x37, 0xe6, 0xd0, 0xe2, 0x5a, 0x5c, 0x56, 0x2b, 0x23, 0x64, + 0x07, 0x01, 0x28, 0xd8, 0x5d, 0x2b, 0x3c, 0xf3, 0x7c, 0xe4, 0xe4, 0x8a, 0x6f, 0x02, 0xc0, 0x0d, + 0x10, 0x49, 0xb1, 0x20, 0x9a, 0x7b, 0x59, 0x2d, 0x4e, 0xa3, 0x4a, 0xc9, 0xe7, 0x21, 0x61, 0xab, + 0xbc, 0xfa, 0x09, 0x84, 0xdd, 0x85, 0x3a, 0x55, 0x9f, 0x14, 0x63, 0x6c, 0x03, 0x9d, 0x35, 0x65, + 0xb5, 0x2a, 0x42, 0xc9, 0x4e, 0x46, 0xaa, 0x4f, 0xe1, 0x7a, 0xaa, 0x7d, 0xba, 0xe1, 0xfb, 0xc6, + 0x85, 0x3e, 0x35, 0xbe, 0xf2, 0x7c, 0xf2, 0x69, 0x64, 0xb5, 0xab, 0x72, 0xb7, 0x35, 0x11, 0xbd, + 0x8f, 0xd8, 0xe7, 0xb2, 0xda, 0xae, 0xe7, 0x93, 0xc3, 0x63, 0x25, 0x2b, 0x62, 0xc9, 0x3a, 0xa7, + 0x01, 0x26, 0x2d, 0x3d, 0xe0, 0xd1, 0x85, 0x5a, 0x85, 0x60, 0x3b, 0x04, 0x42, 0x3d, 0x35, 0x78, + 0xc4, 0xe5, 0xe5, 0xa6, 0x08, 0xf5, 0x79, 0x44, 0x92, 0x90, 0x23, 0x4e, 0x2c, 0xc3, 0xa4, 0xf3, + 0x2b, 0x42, 0xec, 0x59, 0x06, 0x9d, 0x0e, 0x07, 0x8f, 0xf4, 0xd9, 0x3c, 0xe4, 0x61, 0x81, 0x5a, + 0x3e, 0x78, 0x74, 0x30, 0x0f, 0x05, 0xf8, 0xd8, 0x0a, 0xe9, 0xb8, 0x8a, 0xc0, 0x8f, 0xad, 0x10, + 0x95, 0xcd, 0xe0, 0x51, 0xe4, 0xe0, 0xbd, 0x22, 0xfa, 0xf6, 0x91, 0xf0, 0xe0, 0xaa, 0x50, 0x8b, + 0x91, 0xfa, 0x74, 0xce, 0xe3, 0x00, 0xb3, 0x5a, 0x25, 0x22, 0xd8, 0x9f, 0x3b, 0x38, 0xb0, 0x63, + 0x63, 0x7c, 0x62, 0xe9, 0x3e, 0x56, 0xe5, 0x1a, 0x1f, 0x3a, 0x82, 0x68, 0x58, 0x9b, 0x57, 0x80, + 0x27, 0xf4, 0x13, 0x3b, 0x24, 0xc7, 0x4b, 0x56, 0x2b, 0x11, 0x60, 0xcf, 0x0e, 0x71, 0x1d, 0x73, + 0xa4, 0x98, 0x81, 0x94, 0xc5, 0x75, 0x22, 0xda, 0x20, 0xc4, 0x3e, 0xc1, 0x29, 0xa3, 0x6d, 0x50, + 0x52, 0xb4, 0x98, 0xdf, 0x0d, 0x22, 0xad, 0x4b, 0xa4, 0x98, 0xeb, 0x3d, 0xe0, 0xcc, 0x3a, 0x4e, + 0x3d, 0x9e, 0xe7, 0x2b, 0xdc, 0x4a, 0x23, 0x70, 0xdb, 0x0e, 0x4e, 0x29, 0xc7, 0xbb, 0x50, 0x97, + 0xe8, 0x30, 0xbf, 0x9b, 0x7c, 0x66, 0xc4, 0x64, 0xa9, 0x3a, 0xfa, 0xd6, 0xd4, 0x0b, 0x45, 0x33, + 0x5f, 0x95, 0xea, 0xa8, 0x11, 0x3c, 0x5d, 0x47, 0x41, 0x8b, 0x79, 0xde, 0x92, 0xea, 0xc8, 0x49, + 0x31, 0xd7, 0xd7, 0xa0, 0x7a, 0xe6, 0xdb, 0x61, 0x68, 0xb9, 0x7c, 0xf1, 0xdf, 0xe6, 0x1d, 0x2b, + 0x60, 0xb4, 0xfa, 0x5f, 0x83, 0x2a, 0xef, 0x79, 0x21, 0xdf, 0xb6, 0x38, 0x89, 0x80, 0x45, 0x02, + 0x42, 0xf4, 0xc6, 0xd4, 0x76, 0xc9, 0xbb, 0x92, 0xd5, 0xca, 0x1c, 0x82, 0xc6, 0xaa, 0x84, 0x36, + 0xce, 0xc9, 0xc7, 0x92, 0xa0, 0x8d, 0x73, 0x5a, 0x92, 0x33, 0xdb, 0x71, 0xf8, 0xc2, 0xbf, 0x23, + 0x96, 0x24, 0x42, 0x68, 0xdd, 0xc7, 0x68, 0x2a, 0xfd, 0xae, 0x84, 0xa6, 0xb2, 0x71, 0xe2, 0x10, + 0x1a, 0x8b, 0x7e, 0x5d, 0x4c, 0x1c, 0x04, 0x60, 0xc9, 0x09, 0xd2, 0x38, 0x6f, 0xdc, 0x93, 0x91, + 0xc6, 0xb9, 0x90, 0x5b, 0x98, 0x2b, 0xf1, 0xbe, 0x11, 0xcb, 0x2d, 0x04, 0x21, 0xb7, 0x4c, 0x60, + 0x9c, 0x37, 0xb6, 0xd3, 0x04, 0xc6, 0x39, 0x59, 0x48, 0x96, 0x61, 0xf2, 0x8a, 0xbf, 0xc9, 0xb3, + 0x47, 0x00, 0xd5, 0x7b, 0x0b, 0xaa, 0xc1, 0x23, 0x3d, 0xc1, 0xdf, 0xe7, 0xec, 0xc1, 0x23, 0x2d, + 0xa2, 0xb8, 0x0b, 0xf5, 0x78, 0x6a, 0x70, 0x9a, 0xb7, 0xf8, 0xc0, 0x9b, 0x62, 0x6a, 0xd0, 0x71, + 0xdf, 0x4f, 0x33, 0x70, 0x63, 0x40, 0xce, 0x21, 0x12, 0xff, 0xfb, 0x56, 0x10, 0x18, 0xc7, 0xd6, + 0xae, 0xe7, 0xef, 0xce, 0x7f, 0xf2, 0x93, 0x0b, 0xb6, 0x0d, 0x1b, 0x07, 0x86, 0x6f, 0xb9, 0x61, + 0x7c, 0x50, 0x25, 0x3c, 0x33, 0x8b, 0x60, 0xf6, 0x09, 0x28, 0x1c, 0xc4, 0x43, 0xa2, 0x5a, 0xd1, + 0x39, 0xcd, 0xa2, 0x5f, 0x79, 0x89, 0x0a, 0x8d, 0xbd, 0x72, 0xdb, 0x0e, 0x42, 0x0d, 0x2d, 0x7d, + 0xf6, 0x29, 0x28, 0x8e, 0x77, 0x86, 0x16, 0x0b, 0xaa, 0xb1, 0xba, 0xa4, 0x38, 0x8b, 0x5d, 0x32, + 0xd1, 0x96, 0xeb, 0x44, 0x98, 0xa8, 0xbb, 0x9f, 0x82, 0x32, 0x9f, 0xcd, 0xd2, 0xac, 0xeb, 0xcf, + 0x61, 0x25, 0xc2, 0x84, 0xf5, 0x2d, 0xa8, 0x48, 0xa5, 0xae, 0x50, 0xae, 0x21, 0x29, 0x0b, 0x89, + 0xa5, 0x72, 0x56, 0x84, 0x59, 0x42, 0x92, 0xbb, 0xfa, 0xff, 0xad, 0x83, 0x42, 0xce, 0x31, 0x8d, + 0x0e, 0xeb, 0xe9, 0xbc, 0x2b, 0x65, 0x96, 0x65, 0x5e, 0x6a, 0x96, 0x6d, 0x41, 0xde, 0xb1, 0xa7, + 0x71, 0xec, 0x53, 0xea, 0xc0, 0x86, 0x10, 0x38, 0xd6, 0x9e, 0x6f, 0x1f, 0x93, 0x01, 0x29, 0x47, + 0xe9, 0x91, 0xdf, 0x0f, 0xed, 0x31, 0x1a, 0xa2, 0x07, 0x00, 0xa6, 0x1d, 0x84, 0x3a, 0xb9, 0x54, + 0x44, 0xb5, 0x45, 0xcf, 0xc4, 0xfd, 0xaf, 0x95, 0xcd, 0x78, 0x28, 0xb6, 0x41, 0x91, 0x94, 0x30, + 0x37, 0xf6, 0x86, 0xe4, 0xb5, 0x44, 0x39, 0x6b, 0xb9, 0x2d, 0x37, 0x5c, 0xa2, 0x44, 0xf5, 0xa9, + 0xb0, 0x44, 0xd9, 0x35, 0xcf, 0xd5, 0x3f, 0x53, 0x21, 0xd7, 0xf7, 0x4c, 0x8b, 0xbd, 0x07, 0x65, + 0x0a, 0x67, 0x95, 0x06, 0x58, 0x28, 0xc9, 0x88, 0xa6, 0x3f, 0x34, 0x52, 0x25, 0x57, 0xfc, 0x7a, + 0x7e, 0x00, 0xec, 0x6b, 0x64, 0xdc, 0xd1, 0x91, 0x2c, 0x36, 0xa9, 0x22, 0x1c, 0x50, 0xe4, 0x2f, + 0xe1, 0x18, 0xdc, 0x5b, 0xc9, 0x7d, 0xef, 0x5b, 0x2e, 0x69, 0xe4, 0x79, 0x2d, 0x4e, 0x93, 0x49, + 0xed, 0x7b, 0xa8, 0x7a, 0xf1, 0x1d, 0x28, 0xbf, 0xc2, 0xa4, 0xe6, 0x78, 0xda, 0x92, 0xde, 0x83, + 0xf2, 0x57, 0x9e, 0xed, 0xf2, 0x8a, 0x17, 0x96, 0x2a, 0x8e, 0x16, 0x0b, 0xaf, 0xf8, 0x57, 0xe2, + 0x17, 0xbb, 0x03, 0x45, 0xcf, 0xe5, 0x79, 0x17, 0x97, 0xf2, 0x2e, 0x78, 0x6e, 0x8f, 0x47, 0x57, + 0xd5, 0xec, 0x40, 0xf7, 0xed, 0xe3, 0x93, 0x50, 0x47, 0x4e, 0x71, 0x94, 0x5b, 0xb1, 0x03, 0x0d, + 0x61, 0x98, 0x2d, 0x4e, 0xbc, 0x89, 0xed, 0xa0, 0x86, 0x47, 0x99, 0x95, 0x97, 0x32, 0x03, 0x8e, + 0xa6, 0x0c, 0x5f, 0x87, 0xd2, 0xb1, 0xef, 0xa1, 0x11, 0x71, 0xd1, 0x80, 0x25, 0xca, 0x22, 0xe1, + 0x76, 0x2e, 0x50, 0x7d, 0xa2, 0x9f, 0xb6, 0x7b, 0xac, 0x93, 0x97, 0xa4, 0xb2, 0x95, 0xdd, 0x2e, + 0x69, 0xd5, 0x08, 0x48, 0xfe, 0x8f, 0xd7, 0xa1, 0x64, 0x1c, 0x1f, 0xeb, 0x22, 0x48, 0x6c, 0x29, + 0x2f, 0xe3, 0xf8, 0x98, 0x8a, 0x7c, 0x00, 0xb5, 0x33, 0xdb, 0xd5, 0x83, 0x99, 0x35, 0xe6, 0xb4, + 0xb5, 0xe5, 0xae, 0x3c, 0xb3, 0x5d, 0x9c, 0xdd, 0x44, 0x2f, 0x2f, 0x83, 0xfa, 0x37, 0x5f, 0x06, + 0x1b, 0xcf, 0x5b, 0x06, 0x2a, 0x14, 0x84, 0x6b, 0x5f, 0x59, 0x22, 0x11, 0x18, 0xf6, 0x3e, 0x54, + 0x7c, 0xc3, 0x3d, 0xd5, 0xc5, 0xb9, 0xf8, 0x97, 0xb2, 0xa5, 0xaf, 0x19, 0xee, 0xa9, 0x38, 0x16, + 0x07, 0x3f, 0xfe, 0x9d, 0x56, 0x99, 0x37, 0x5f, 0x62, 0x23, 0x49, 0xa6, 0x17, 0x7b, 0xb1, 0xe9, + 0xf5, 0x21, 0xd9, 0x38, 0x96, 0x1b, 0xea, 0x11, 0xc3, 0xa5, 0xd5, 0x0c, 0x55, 0x4e, 0x36, 0xe0, + 0x6c, 0xd8, 0x00, 0xf2, 0xb4, 0xe9, 0xe4, 0x96, 0xbb, 0x9c, 0x6a, 0x40, 0xec, 0x82, 0xd3, 0xc0, + 0x4f, 0xdc, 0x71, 0x4d, 0xd8, 0x48, 0x22, 0x67, 0x79, 0x08, 0xf2, 0x15, 0xd9, 0xd5, 0x9f, 0x0a, + 0xb5, 0x8d, 0xac, 0x2a, 0x3b, 0x15, 0x7f, 0x7b, 0x07, 0x6a, 0x3c, 0x10, 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, 0x47, 0xb6, + 0x6b, 0xe2, 0xd4, 0x0b, 0x8d, 0xe3, 0xa0, 0xd1, 0xa0, 0x95, 0x59, 0x11, 0xb0, 0x91, 0x71, 0x1c, + 0xa0, 0xe1, 0x6f, 0x70, 0x63, 0x83, 0xd7, 0xfb, 0xba, 0xec, 0x99, 0x92, 0xcc, 0x10, 0xad, 0x62, + 0x48, 0x36, 0xc9, 0xc7, 0xc0, 0xa2, 0x93, 0x47, 0xc9, 0x8e, 0xbf, 0xb1, 0x34, 0x1b, 0x37, 0xc4, + 0xd1, 0x63, 0x6c, 0xc5, 0xdf, 0x86, 0x0a, 0xf7, 0x54, 0xe8, 0x41, 0x68, 0xcd, 0x1a, 0xaf, 0x50, + 0x85, 0x80, 0x83, 0x86, 0xa1, 0x35, 0x63, 0x1f, 0x43, 0x2d, 0x6d, 0x65, 0xdd, 0x5c, 0x71, 0x80, + 0x47, 0xd3, 0x42, 0xab, 0x8e, 0x65, 0xbb, 0xeb, 0x0e, 0x8f, 0xea, 0x26, 0x0d, 0x89, 0x18, 0xf9, + 0x21, 0x55, 0xd5, 0xf5, 0xc2, 0x56, 0x04, 0xc3, 0x0e, 0x8c, 0x0c, 0xec, 0xf0, 0x9c, 0x94, 0xaa, + 0xb8, 0x03, 0x63, 0xd3, 0x11, 0x8d, 0xa3, 0xc8, 0x8a, 0x6c, 0x03, 0x0f, 0xbc, 0xa0, 0x4d, 0xde, + 0xf2, 0x79, 0x90, 0x05, 0xe9, 0x50, 0xf1, 0x99, 0xde, 0xe2, 0xde, 0xa3, 0xf1, 0x80, 0x14, 0x79, + 0x37, 0xfa, 0x04, 0xea, 0x33, 0xdf, 0xd2, 0xa5, 0x92, 0x55, 0xb9, 0x51, 0x07, 0xbe, 0x95, 0x14, + 0x5e, 0x9d, 0x49, 0x29, 0xf6, 0x03, 0xd8, 0x94, 0x38, 0xe7, 0xa7, 0xc4, 0x7c, 0x87, 0x98, 0x2f, + 0x2f, 0x30, 0x1f, 0x9e, 0x22, 0x7b, 0x7d, 0x96, 0x4a, 0xb3, 0xcb, 0x90, 0xef, 0x06, 0x1d, 0xd7, + 0x24, 0xdd, 0xaa, 0xa4, 0xf1, 0x04, 0x7b, 0x04, 0x55, 0x6e, 0xc8, 0x50, 0x9c, 0x6a, 0xd0, 0xb8, + 0x27, 0xbb, 0x9e, 0xc9, 0x9a, 0x21, 0x84, 0x56, 0x71, 0xe2, 0xdf, 0x01, 0xfb, 0x08, 0x36, 0xf9, + 0xb9, 0x80, 0x2c, 0x22, 0xdf, 0x58, 0x1e, 0x72, 0x22, 0xda, 0x4d, 0xe4, 0xa4, 0x06, 0xd7, 0xfd, + 0xb9, 0x4b, 0xc6, 0x8d, 0xe0, 0xe4, 0x1e, 0x2b, 0xe2, 0xdf, 0x26, 0xfe, 0x6b, 0x62, 0x75, 0x71, + 0x32, 0xce, 0x4b, 0xb2, 0xe9, 0xaa, 0x2f, 0x83, 0x0e, 0x90, 0xef, 0x39, 0x79, 0x72, 0x4f, 0x0e, + 0xe5, 0xf9, 0xe6, 0xb7, 0xc9, 0x93, 0xbc, 0x3c, 0x94, 0x27, 0x83, 0xdc, 0x7c, 0x6e, 0x9b, 0xa4, + 0xe9, 0x55, 0x35, 0xfa, 0xcd, 0x5e, 0x87, 0xba, 0x6f, 0x8d, 0xe7, 0x7e, 0x60, 0x3f, 0xb3, 0xf4, + 0xc0, 0x76, 0x4f, 0x49, 0xc7, 0x2b, 0x69, 0xb5, 0x18, 0x3a, 0xb4, 0xdd, 0x53, 0x14, 0x19, 0xd6, + 0x79, 0x68, 0xf9, 0x2e, 0x0f, 0x9d, 0x7f, 0x5b, 0x16, 0x19, 0x1d, 0x42, 0xe0, 0x3a, 0xd7, 0xc0, + 0x8a, 0x7f, 0x2f, 0x8c, 0x6c, 0xc0, 0x47, 0xf6, 0xc1, 0x37, 0x1a, 0xd9, 0x21, 0x8d, 0xec, 0x3d, + 0x28, 0xd9, 0x6e, 0x68, 0xf9, 0xcf, 0x0c, 0xa7, 0xf1, 0xee, 0x92, 0x34, 0x8e, 0x71, 0xec, 0x2e, + 0x14, 0x03, 0xc7, 0xc6, 0xf5, 0xde, 0x78, 0x6f, 0x89, 0x2c, 0x42, 0xb1, 0x6d, 0x28, 0xc7, 0xb7, + 0xbe, 0x1a, 0xef, 0x2f, 0xd1, 0x25, 0x48, 0x76, 0x0b, 0x72, 0x67, 0x38, 0xa1, 0x1e, 0x2e, 0x1f, + 0x15, 0x20, 0x1c, 0xb7, 0xef, 0x09, 0xea, 0xec, 0xb4, 0x7d, 0x3f, 0x5a, 0xda, 0xbe, 0x77, 0x6d, + 0xc7, 0xe1, 0xdb, 0xf7, 0x44, 0xfc, 0xc2, 0xcd, 0x8f, 0x38, 0xb0, 0x25, 0x1f, 0x2c, 0x6f, 0x7e, + 0x88, 0x7b, 0x42, 0xf7, 0xe3, 0x2a, 0x01, 0xf9, 0xbf, 0xb9, 0x1b, 0xff, 0x43, 0xb9, 0xaf, 0xd2, + 0x8e, 0x71, 0x0d, 0x82, 0x38, 0x8d, 0x06, 0x88, 0xf0, 0xfe, 0xa3, 0xd2, 0xf4, 0x11, 0xbf, 0x78, + 0xc1, 0x21, 0x5d, 0xf3, 0x9c, 0xbd, 0x07, 0xb5, 0x28, 0x5c, 0x08, 0x8b, 0x0b, 0x1a, 0x1f, 0x2f, + 0xd5, 0x20, 0x4d, 0xc0, 0xda, 0x50, 0x9d, 0xa0, 0xee, 0x3e, 0xe5, 0xaa, 0x7c, 0xe3, 0x13, 0xaa, + 0xc8, 0x56, 0xb4, 0xb1, 0x3e, 0x4f, 0xd5, 0xd7, 0x52, 0x5c, 0xec, 0x01, 0x30, 0x7b, 0xc2, 0xc7, + 0x73, 0xd7, 0xf7, 0xa6, 0x5c, 0x5d, 0x6f, 0x7c, 0xca, 0x3d, 0x68, 0xcb, 0x18, 0x3a, 0x0c, 0xb4, + 0x5c, 0x53, 0x9f, 0x06, 0x42, 0x4d, 0xf8, 0x1e, 0xd5, 0x53, 0x08, 0xaf, 0xf8, 0x76, 0x68, 0xe4, + 0xec, 0x45, 0xda, 0xfd, 0x80, 0x6b, 0x0d, 0x9f, 0x02, 0x4e, 0xd7, 0x67, 0x09, 0xeb, 0xaf, 0xbc, + 0x90, 0x15, 0x69, 0x23, 0xd6, 0x4f, 0xa0, 0xce, 0x1d, 0xa5, 0xa4, 0x91, 0xe1, 0x14, 0xfd, 0xbe, + 0x2c, 0xb9, 0x64, 0x17, 0xb2, 0x56, 0x35, 0x65, 0x87, 0xf2, 0xc7, 0xb0, 0x11, 0xf9, 0x7a, 0x43, + 0xe1, 0x16, 0xfe, 0x55, 0xb9, 0xd8, 0xd8, 0x97, 0xaa, 0xd5, 0xe6, 0xd1, 0x4f, 0x2a, 0xf2, 0x11, + 0xd4, 0x68, 0x17, 0x0d, 0x5c, 0x63, 0x16, 0x9c, 0x78, 0x61, 0xe3, 0xd7, 0x64, 0x85, 0x60, 0x28, + 0xa0, 0x5a, 0x15, 0x89, 0xa2, 0x14, 0x0a, 0xff, 0x64, 0x9d, 0x8e, 0x43, 0xab, 0xf1, 0x03, 0x2e, + 0xfc, 0x63, 0x60, 0x2b, 0xb4, 0xd8, 0x23, 0x00, 0x63, 0x36, 0x73, 0x2e, 0xf8, 0xd4, 0xfc, 0x21, + 0x4d, 0xcd, 0xcb, 0xd2, 0xd4, 0x6c, 0x22, 0x92, 0xe6, 0x66, 0xd9, 0x88, 0x7e, 0xb2, 0x87, 0x50, + 0x9d, 0x79, 0x41, 0xa8, 0x9b, 0x53, 0x87, 0xda, 0xdf, 0x94, 0xd7, 0xf6, 0x81, 0x17, 0x84, 0xed, + 0xa9, 0x83, 0xad, 0x80, 0x59, 0xfc, 0x9b, 0xf5, 0xe0, 0x92, 0xe7, 0xea, 0xe6, 0x7c, 0xe6, 0xd8, + 0x63, 0xec, 0x01, 0x83, 0x8e, 0xde, 0x1b, 0x3b, 0x54, 0xe2, 0x4d, 0xa9, 0xc4, 0x81, 0xdb, 0x8e, + 0x88, 0x44, 0xec, 0xda, 0xa6, 0xb7, 0x08, 0x22, 0x3b, 0x93, 0xc6, 0x20, 0x0e, 0xe0, 0x6c, 0x71, + 0xd5, 0x80, 0xa0, 0x51, 0x04, 0xe7, 0x27, 0xb0, 0x91, 0x50, 0x61, 0x03, 0x83, 0x46, 0x5b, 0x9e, + 0xc9, 0x52, 0x44, 0x78, 0x2d, 0x62, 0x44, 0x58, 0x40, 0x7d, 0xe7, 0x39, 0xce, 0x7c, 0x26, 0x44, + 0x69, 0xa3, 0x23, 0xfa, 0x8e, 0x80, 0x5c, 0x4a, 0x4a, 0xa6, 0xb8, 0x35, 0x6d, 0xec, 0xca, 0xa6, + 0xb8, 0x35, 0x45, 0x35, 0x43, 0x44, 0xe7, 0x3e, 0xb3, 0xad, 0xb3, 0xa0, 0xf1, 0x98, 0x22, 0x37, + 0x45, 0xfc, 0xf3, 0x13, 0x04, 0xe1, 0xbe, 0x6f, 0xda, 0x3e, 0x9a, 0x00, 0x14, 0x74, 0xb5, 0xc7, + 0x03, 0x68, 0x39, 0x08, 0x29, 0x58, 0x0b, 0x1a, 0x24, 0xce, 0xc5, 0x5a, 0x4d, 0x9d, 0xc1, 0x75, + 0x97, 0x96, 0xe4, 0x15, 0xa4, 0xe5, 0x47, 0x7c, 0x07, 0xd2, 0x69, 0xdc, 0x0f, 0xe1, 0xd5, 0xd5, + 0x99, 0x44, 0xb7, 0x13, 0x3f, 0x23, 0x7d, 0xe3, 0xfa, 0x0a, 0xee, 0x16, 0x5d, 0x58, 0x54, 0x7f, + 0x96, 0x87, 0x52, 0x64, 0x1a, 0xb1, 0x0a, 0x14, 0x0f, 0xfb, 0x9f, 0xf7, 0x07, 0x4f, 0xfb, 0xfc, + 0x92, 0x5e, 0x73, 0x38, 0xec, 0x68, 0x23, 0xc5, 0x64, 0x75, 0x00, 0xba, 0x86, 0xa3, 0x0f, 0x5b, + 0xcd, 0x3e, 0xbf, 0xb4, 0x47, 0x97, 0x7f, 0x78, 0x7a, 0x9d, 0x6d, 0x42, 0x6d, 0xf7, 0xb0, 0x4f, + 0xf1, 0x82, 0x1c, 0x94, 0x45, 0x50, 0xe7, 0x0b, 0x7e, 0xc2, 0xc9, 0x41, 0x39, 0x04, 0xed, 0x37, + 0x47, 0x1d, 0xad, 0x1b, 0x81, 0xf2, 0x14, 0x7a, 0x38, 0x38, 0xd4, 0x5a, 0x22, 0xa7, 0x02, 0xbb, + 0x02, 0x9b, 0x31, 0x5b, 0x94, 0xa5, 0x52, 0xc4, 0x9a, 0x1d, 0x68, 0x83, 0xcf, 0x3a, 0xad, 0x91, + 0x02, 0x74, 0x5c, 0xfa, 0xf8, 0xb1, 0x52, 0x61, 0x55, 0x28, 0xb5, 0xbb, 0xc3, 0x51, 0xb7, 0xdf, + 0x1a, 0x29, 0x55, 0xac, 0xf0, 0x6e, 0xb7, 0x37, 0xea, 0x68, 0x4a, 0x8d, 0x95, 0x20, 0xf7, 0xd9, + 0xa0, 0xdb, 0x57, 0xea, 0x74, 0x1d, 0xa9, 0xb9, 0x7f, 0xd0, 0xeb, 0x28, 0x1b, 0x08, 0x1d, 0x0e, + 0xb4, 0x91, 0xa2, 0x20, 0xf4, 0x69, 0xb7, 0xdf, 0x1e, 0x3c, 0x55, 0x36, 0x59, 0x19, 0xf2, 0x87, + 0x7d, 0x2c, 0x86, 0xb1, 0x1a, 0x94, 0xe9, 0xa7, 0xde, 0xec, 0xf5, 0x94, 0x4b, 0xd2, 0x19, 0xeb, + 0x65, 0x44, 0xd1, 0x89, 0xed, 0x10, 0xeb, 0x70, 0x05, 0xdb, 0x12, 0x27, 0x89, 0xfa, 0x2a, 0xe6, + 0xb3, 0xdf, 0xed, 0x1f, 0x0e, 0x95, 0x6b, 0x48, 0x4c, 0x3f, 0x09, 0xd3, 0xc0, 0x7c, 0xba, 0x7d, + 0xea, 0xca, 0x5b, 0xf8, 0xbb, 0xdd, 0xe9, 0x75, 0x46, 0x1d, 0xe5, 0x36, 0xb6, 0xaa, 0x37, 0x68, + 0x7d, 0xae, 0x0f, 0x0e, 0x94, 0xd7, 0xb0, 0x4f, 0x0f, 0xb4, 0x8e, 0x2e, 0x08, 0xef, 0x30, 0x05, + 0xaa, 0xbb, 0x87, 0x3f, 0xfe, 0xf1, 0x97, 0xba, 0x68, 0xd4, 0xeb, 0x58, 0x66, 0x42, 0xa1, 0x1f, + 0x7e, 0xae, 0xdc, 0x5b, 0x00, 0x0d, 0x3f, 0x57, 0xde, 0xc0, 0x4e, 0x89, 0x7a, 0x59, 0xd9, 0x46, + 0x02, 0xad, 0xd3, 0x3a, 0xd4, 0x86, 0xdd, 0x27, 0x1d, 0xbd, 0x35, 0xea, 0x28, 0x6f, 0x52, 0x2f, + 0x74, 0xfb, 0x9f, 0x2b, 0xf7, 0xb1, 0x9a, 0xf8, 0x8b, 0xf7, 0xfd, 0x5b, 0x8c, 0x41, 0x3d, 0xa1, + 0x25, 0xd8, 0xdb, 0x48, 0xb2, 0xa3, 0x0d, 0x9a, 0xed, 0x56, 0x73, 0x38, 0x52, 0xde, 0xc1, 0x36, + 0x0e, 0x0f, 0x7a, 0xdd, 0x91, 0xf2, 0x00, 0x1b, 0xf2, 0xb8, 0x39, 0xda, 0xeb, 0x68, 0xca, 0xbb, + 0x38, 0x8c, 0xa3, 0xee, 0x7e, 0x47, 0x17, 0x7d, 0xfa, 0x10, 0xcb, 0xd8, 0xed, 0xf6, 0x7a, 0xca, + 0x23, 0x3a, 0x23, 0x6c, 0x6a, 0xa3, 0x2e, 0x0d, 0xe4, 0x07, 0x98, 0x41, 0xf3, 0xe0, 0xa0, 0xf7, + 0xa5, 0xf2, 0x21, 0x36, 0x70, 0xff, 0xb0, 0x37, 0xea, 0xea, 0x87, 0x07, 0xed, 0xe6, 0xa8, 0xa3, + 0x7c, 0x44, 0xa3, 0x3c, 0x18, 0x8e, 0xda, 0xfb, 0x3d, 0xe5, 0x63, 0xca, 0x93, 0xe6, 0x58, 0xab, + 0x37, 0xe8, 0x77, 0x94, 0x4f, 0xd4, 0x5c, 0x69, 0x4b, 0xd9, 0x52, 0x73, 0x25, 0x55, 0x51, 0xd5, + 0xdf, 0x84, 0x52, 0x64, 0x17, 0x63, 0x96, 0xdd, 0x7e, 0xbf, 0xa3, 0x29, 0x6b, 0x58, 0x6c, 0xaf, + 0xb3, 0x3b, 0x52, 0x32, 0x74, 0x78, 0xda, 0x7d, 0xbc, 0x37, 0x52, 0xd6, 0xf1, 0xe7, 0xe0, 0x10, + 0x7b, 0x30, 0x4b, 0x4d, 0xef, 0xec, 0x77, 0x95, 0x1c, 0xfe, 0x6a, 0xf6, 0x47, 0x5d, 0x25, 0x4f, + 0x13, 0xa4, 0xdb, 0x7f, 0xdc, 0xeb, 0x28, 0x05, 0x84, 0xee, 0x37, 0xb5, 0xcf, 0x95, 0x22, 0xcf, + 0xb4, 0xdd, 0xf9, 0x42, 0x29, 0xb1, 0x02, 0xac, 0xf7, 0x1e, 0x2a, 0x65, 0x04, 0xb5, 0x3b, 0xed, + 0xc3, 0x03, 0x05, 0xd4, 0x6d, 0x28, 0x36, 0x8f, 0x8f, 0xf7, 0x3d, 0x93, 0xce, 0x6b, 0x77, 0x0f, + 0x7b, 0x3d, 0xbe, 0x60, 0x76, 0x06, 0xa3, 0xd1, 0x60, 0x5f, 0xc9, 0xe0, 0x14, 0x1d, 0x0d, 0x0e, + 0x94, 0x75, 0xb5, 0x0b, 0xa5, 0x48, 0x03, 0x90, 0xee, 0xd2, 0x95, 0x20, 0x77, 0xa0, 0x75, 0x9e, + 0xf0, 0x13, 0xff, 0x7e, 0xe7, 0x0b, 0xac, 0x26, 0xfe, 0xc2, 0x8c, 0xb2, 0x58, 0x10, 0xbf, 0xf4, + 0x46, 0x97, 0xe9, 0x7a, 0xdd, 0x7e, 0xa7, 0xa9, 0x29, 0x79, 0xf5, 0x43, 0xd8, 0x5c, 0x92, 0x9f, + 0x54, 0x7c, 0xb3, 0x2b, 0x8a, 0xef, 0x3e, 0xee, 0x0f, 0xb4, 0x0e, 0xbf, 0x9d, 0x27, 0x3a, 0x75, + 0x5d, 0x7d, 0x0b, 0xca, 0xb1, 0xa0, 0xc7, 0x49, 0xd6, 0xd2, 0x06, 0xc3, 0x21, 0x1f, 0x83, 0x35, + 0x4c, 0x53, 0xdf, 0xf0, 0x74, 0xe6, 0xb3, 0x5c, 0xe9, 0xb6, 0xb2, 0xf5, 0x59, 0xae, 0x74, 0x57, + 0x79, 0x5d, 0x1d, 0xc2, 0x66, 0xb4, 0xdf, 0xd0, 0xcd, 0x00, 0x32, 0x84, 0x2e, 0x43, 0xbe, 0x67, + 0x3d, 0xb3, 0x9c, 0x28, 0xc4, 0x9d, 0x12, 0x08, 0x1d, 0x1c, 0x7d, 0xd5, 0x8d, 0x2f, 0x53, 0x53, + 0x02, 0x15, 0xcc, 0xbe, 0x74, 0x9f, 0x9b, 0xae, 0x2b, 0xfe, 0x95, 0x0c, 0x94, 0xe2, 0x5d, 0xec, + 0x2e, 0xac, 0x8f, 0x86, 0xe2, 0xc8, 0xea, 0xf2, 0x83, 0xe4, 0xf9, 0x8a, 0x51, 0xf4, 0x4b, 0x5b, + 0x1f, 0x0d, 0xd9, 0xdb, 0x50, 0xe0, 0xd7, 0x4f, 0x85, 0xbb, 0xea, 0x72, 0x7a, 0x67, 0x1c, 0x11, + 0x4e, 0x13, 0x34, 0xec, 0x43, 0x28, 0xc7, 0xb5, 0x15, 0xfe, 0x9b, 0x6b, 0x69, 0x86, 0x18, 0xad, + 0x25, 0x94, 0x6a, 0x0f, 0xea, 0xe9, 0x0c, 0xd9, 0x2d, 0x00, 0x9e, 0xa5, 0xe4, 0xa4, 0x94, 0x20, + 0xec, 0x06, 0x44, 0xb7, 0x62, 0xdb, 0x54, 0xb1, 0x5a, 0x7c, 0x4b, 0xb6, 0xad, 0xfe, 0xab, 0x2c, + 0x40, 0xa2, 0x07, 0x63, 0x47, 0xc4, 0x5e, 0xa9, 0xbc, 0x38, 0x93, 0x7f, 0x05, 0xca, 0x8e, 0x67, + 0x98, 0xf2, 0xeb, 0x15, 0x25, 0x04, 0xd0, 0x30, 0xc9, 0x37, 0xc4, 0xca, 0x3c, 0x20, 0x86, 0x5d, + 0x85, 0xc2, 0xc4, 0xf3, 0xa7, 0x46, 0x28, 0xee, 0x33, 0x88, 0x14, 0x6e, 0x67, 0xf6, 0xb1, 0xeb, + 0xf9, 0x16, 0x5a, 0x03, 0x2e, 0x5d, 0x69, 0xc0, 0x31, 0xa8, 0x0a, 0x60, 0x0f, 0x61, 0xb8, 0x1d, + 0x59, 0xee, 0xd8, 0xf1, 0x02, 0xcb, 0xc4, 0x0d, 0xa6, 0x40, 0x2a, 0x3f, 0x44, 0xa0, 0x9d, 0x0b, + 0xde, 0x5a, 0x7f, 0x6a, 0xbb, 0x46, 0x28, 0x8e, 0xa9, 0xa8, 0xb5, 0x11, 0x04, 0xab, 0xfb, 0x55, + 0xe0, 0x09, 0x27, 0x15, 0xbf, 0x0e, 0x55, 0x42, 0x00, 0x55, 0xf7, 0x55, 0x00, 0x2b, 0x18, 0x1b, + 0x33, 0x9e, 0x79, 0x99, 0x32, 0x2f, 0x0b, 0xc8, 0xce, 0x05, 0xeb, 0x41, 0x7d, 0x74, 0x84, 0xdb, + 0xaf, 0xd7, 0x36, 0x42, 0xa3, 0xe5, 0x39, 0xc2, 0x7d, 0x74, 0x77, 0xd1, 0x60, 0x78, 0x90, 0x26, + 0xe3, 0x11, 0x9f, 0x0b, 0xbc, 0x68, 0xc0, 0xdb, 0x63, 0xeb, 0xc8, 0xf2, 0x8f, 0xb9, 0xf1, 0x51, + 0x49, 0x5d, 0x44, 0xe0, 0x18, 0xb2, 0x3e, 0x2a, 0x76, 0x92, 0xb8, 0xd1, 0x84, 0x4b, 0x2b, 0x32, + 0xff, 0x56, 0xa1, 0x85, 0xff, 0x6c, 0x1d, 0x2a, 0x52, 0xfe, 0x74, 0x4e, 0x60, 0xcc, 0xc8, 0xcf, + 0x15, 0xc7, 0x4c, 0x97, 0x05, 0x84, 0x5f, 0xfb, 0x18, 0x1b, 0xa1, 0xe1, 0x78, 0xc7, 0xd2, 0x85, + 0x16, 0x01, 0xe9, 0x9a, 0x74, 0x74, 0x67, 0x4c, 0xad, 0x60, 0x66, 0x8c, 0xa3, 0x15, 0x92, 0x00, + 0x92, 0x67, 0x53, 0x72, 0xf2, 0xb3, 0x29, 0x0a, 0x3f, 0xe3, 0xe5, 0x41, 0x32, 0x74, 0xa4, 0x7b, + 0x1b, 0x2a, 0x91, 0xde, 0x88, 0xa5, 0x88, 0x63, 0xc8, 0x08, 0xd4, 0x35, 0xd9, 0x3d, 0x48, 0x9e, + 0x85, 0xd1, 0x8d, 0x40, 0xf7, 0x26, 0x51, 0x84, 0x5e, 0x0c, 0x6e, 0x06, 0x83, 0x49, 0x7c, 0x36, + 0x30, 0xf5, 0xcc, 0x78, 0x7c, 0x11, 0x40, 0x32, 0xee, 0x01, 0x5c, 0x12, 0x0e, 0x4b, 0xcb, 0xd4, + 0x27, 0xb6, 0xe5, 0x98, 0xba, 0x6d, 0xf2, 0x13, 0xf4, 0xbc, 0xb6, 0x19, 0xa3, 0x76, 0x11, 0xd3, + 0x35, 0x49, 0xc3, 0x12, 0x56, 0xaa, 0x69, 0x1f, 0x5b, 0x41, 0x28, 0xe2, 0xc9, 0xaa, 0x1c, 0xd8, + 0x26, 0x98, 0xea, 0x44, 0x4b, 0xa4, 0x19, 0x86, 0x74, 0xe5, 0x2c, 0x56, 0xf7, 0x32, 0xd1, 0x6d, + 0x14, 0xae, 0xe9, 0xbd, 0x42, 0xb1, 0x58, 0x22, 0x50, 0x58, 0xac, 0x94, 0x38, 0x00, 0xf8, 0x1e, + 0x6c, 0x20, 0x52, 0x54, 0x8a, 0x48, 0xf8, 0x35, 0xa4, 0xda, 0xd8, 0x73, 0x78, 0x85, 0x10, 0xa8, + 0xfe, 0xd5, 0x3c, 0x40, 0x62, 0xe8, 0xd3, 0x0d, 0x37, 0xf2, 0xc0, 0xc5, 0x23, 0x57, 0xa4, 0x74, + 0xd7, 0x64, 0x0f, 0xe1, 0xaa, 0xb8, 0x4f, 0x17, 0x07, 0x79, 0xd8, 0xae, 0x7e, 0x64, 0x44, 0x41, + 0x65, 0x4c, 0x60, 0xf9, 0xf1, 0x7d, 0xd7, 0xdd, 0x31, 0xd0, 0x6c, 0xd8, 0x90, 0x79, 0xc2, 0x8b, + 0x59, 0xda, 0xe3, 0x2f, 0x2b, 0xa3, 0x09, 0xfb, 0xe8, 0x62, 0xc6, 0xde, 0x83, 0x2b, 0xbe, 0x35, + 0xf1, 0xad, 0xe0, 0x44, 0x0f, 0x03, 0xb9, 0x30, 0x1e, 0xf5, 0xb9, 0x29, 0x90, 0xa3, 0x20, 0x2e, + 0xeb, 0x3d, 0xb8, 0x22, 0x3a, 0x77, 0xa1, 0x7a, 0xdc, 0x93, 0xbe, 0xc9, 0x91, 0x72, 0xed, 0x28, + 0xc4, 0x97, 0xbc, 0x1f, 0xd1, 0x43, 0x3c, 0x25, 0xad, 0xcc, 0x3d, 0x1d, 0xe2, 0x7a, 0x3a, 0xb9, + 0x30, 0x68, 0xe1, 0x96, 0x34, 0x9e, 0x60, 0x2a, 0xe4, 0x70, 0xec, 0x69, 0xe8, 0xea, 0x0f, 0xeb, + 0x0f, 0xe8, 0xa1, 0x21, 0x7a, 0x2e, 0xc0, 0x33, 0x2d, 0x8d, 0x70, 0xec, 0x1d, 0x9c, 0x17, 0x49, + 0xb3, 0xa3, 0xb7, 0x32, 0x78, 0x9c, 0x83, 0x92, 0x34, 0x54, 0xe3, 0xaf, 0x66, 0xbc, 0x05, 0x4c, + 0xaa, 0x79, 0x44, 0x5d, 0x25, 0xea, 0x8d, 0xb8, 0xda, 0x82, 0xf8, 0x0d, 0xa0, 0x2a, 0xf2, 0x63, + 0xb4, 0xda, 0xb2, 0xbd, 0x8f, 0x48, 0x3a, 0x51, 0x7b, 0x0f, 0xae, 0x24, 0xad, 0xd3, 0x8d, 0x50, + 0x0f, 0x4f, 0x2c, 0xdd, 0x72, 0x4d, 0xba, 0x04, 0x59, 0xd2, 0x36, 0xe3, 0x86, 0x36, 0xc3, 0xd1, + 0x89, 0x85, 0x16, 0xbb, 0xe4, 0x51, 0xdd, 0x78, 0xb1, 0x47, 0xf5, 0x23, 0x68, 0xa4, 0x75, 0x6a, + 0xa9, 0xbb, 0xf9, 0x25, 0xe2, 0xcb, 0x72, 0xac, 0x48, 0xdc, 0xe3, 0xf7, 0x61, 0xf3, 0xc4, 0x08, + 0xd2, 0xfa, 0x38, 0x39, 0x7a, 0x4b, 0xda, 0xc6, 0x89, 0x11, 0xc8, 0x4a, 0xb8, 0xfa, 0xfb, 0x19, + 0xa8, 0xa7, 0x5d, 0x1f, 0xfc, 0x66, 0x96, 0x33, 0x9f, 0xba, 0x3c, 0xe0, 0x31, 0xaf, 0x45, 0x49, + 0x5c, 0x0b, 0x3c, 0xa0, 0x64, 0x3e, 0x75, 0xa3, 0xb5, 0x30, 0x3b, 0x6d, 0x51, 0x9a, 0xbd, 0x09, + 0xc5, 0xd9, 0x29, 0x97, 0xd0, 0xcf, 0x9b, 0x7d, 0x85, 0x19, 0x0f, 0x70, 0x7f, 0x13, 0x8a, 0x73, + 0x41, 0x9a, 0x7b, 0x1e, 0xe9, 0x9c, 0x48, 0xd5, 0x7f, 0xbe, 0x0e, 0x55, 0xd9, 0x63, 0xf7, 0x4d, + 0x22, 0x4d, 0xbe, 0x55, 0x10, 0xd0, 0x16, 0xc5, 0xaa, 0xea, 0x14, 0x4d, 0x8f, 0xfd, 0xc4, 0xc3, + 0x4c, 0xe0, 0xc4, 0x08, 0x9a, 0xf3, 0xd0, 0x43, 0x91, 0x8f, 0xa2, 0xd4, 0x73, 0xa2, 0x28, 0x7b, + 0xbe, 0x32, 0x50, 0x26, 0x88, 0x00, 0xfb, 0xf7, 0xc4, 0x25, 0x1e, 0xba, 0xb6, 0x47, 0x61, 0x9d, + 0xf9, 0xa5, 0xf9, 0x52, 0x8d, 0x6e, 0xed, 0x51, 0x18, 0xeb, 0x43, 0xd8, 0x48, 0xae, 0x4c, 0x70, + 0x96, 0xc2, 0x12, 0x4b, 0x2d, 0xbe, 0x2f, 0x21, 0x9e, 0x14, 0xa8, 0xd9, 0x81, 0xee, 0x39, 0x66, + 0x74, 0x37, 0xab, 0x18, 0x9d, 0xa7, 0x0c, 0x1c, 0x53, 0xdc, 0x15, 0xe5, 0x34, 0xae, 0x75, 0x16, + 0xd1, 0xc4, 0x67, 0x2e, 0x7d, 0xeb, 0x4c, 0xdc, 0xd1, 0xfa, 0xf3, 0x0c, 0x6c, 0x2e, 0x39, 0xe9, + 0x50, 0xb4, 0x27, 0x0f, 0x5c, 0xe1, 0x4f, 0x34, 0x32, 0xa7, 0x46, 0x38, 0x3e, 0xd1, 0x67, 0xbe, + 0x35, 0xb1, 0xcf, 0xa3, 0x57, 0xba, 0x08, 0x76, 0x40, 0x20, 0x94, 0xfe, 0xfc, 0xdc, 0x90, 0x1f, + 0x63, 0x70, 0xc1, 0xc7, 0xcf, 0x0a, 0x7b, 0x74, 0x7e, 0x11, 0x85, 0xb9, 0xe6, 0x9e, 0x13, 0xe6, + 0x7a, 0x03, 0xca, 0xae, 0x47, 0x21, 0x4d, 0xb3, 0x53, 0x11, 0x0b, 0x56, 0x74, 0xbd, 0x70, 0xe0, + 0x1e, 0x9c, 0xb2, 0x87, 0x70, 0x65, 0x1e, 0x50, 0xdc, 0xc0, 0x91, 0xe5, 0x07, 0x27, 0x76, 0x6c, + 0x2e, 0x73, 0x01, 0x72, 0x69, 0x1e, 0x58, 0xfb, 0x31, 0x8e, 0xb7, 0x44, 0xbd, 0x09, 0x85, 0x6e, + 0xec, 0x5c, 0x8c, 0xe3, 0xea, 0xb2, 0xe2, 0x69, 0x1b, 0x0f, 0xca, 0xdc, 0xea, 0xdc, 0x37, 0x66, + 0xec, 0x3e, 0x64, 0xa7, 0xc6, 0x4c, 0x1c, 0x56, 0x36, 0xe2, 0x13, 0x5d, 0x8e, 0x7d, 0xb0, 0x6f, + 0xcc, 0xf8, 0xee, 0x8f, 0x44, 0x37, 0x3e, 0x82, 0x52, 0x04, 0xf8, 0x56, 0x3b, 0xf6, 0xbf, 0x5d, + 0x87, 0x72, 0x5b, 0x3e, 0x1c, 0x18, 0x1b, 0xae, 0x1e, 0xfa, 0x73, 0x17, 0x15, 0xe8, 0xe8, 0xc1, + 0x8f, 0xb1, 0xe1, 0x8e, 0x04, 0x28, 0x9a, 0xda, 0xeb, 0x2f, 0x98, 0xda, 0x37, 0x01, 0x7c, 0xf2, + 0xad, 0x91, 0x7b, 0x2d, 0x1b, 0xc7, 0x19, 0x77, 0xcd, 0xae, 0x79, 0xbe, 0x3a, 0xc4, 0x2a, 0xf7, + 0xcd, 0x43, 0xac, 0xf2, 0x2b, 0x43, 0xac, 0xee, 0x25, 0xdb, 0x0b, 0x4e, 0x71, 0x2c, 0xb8, 0xcc, + 0x37, 0xb9, 0x59, 0x7c, 0x65, 0x09, 0x4b, 0xff, 0x1e, 0xd4, 0xa3, 0xd6, 0x89, 0xfc, 0x20, 0x75, + 0x4b, 0x4a, 0xe0, 0xf8, 0x69, 0x42, 0x2d, 0x94, 0x93, 0xe9, 0x25, 0x5b, 0x79, 0x49, 0x18, 0xd7, + 0x5f, 0xcf, 0x00, 0x13, 0xbe, 0xa0, 0xdd, 0xb9, 0xe3, 0x8c, 0xac, 0x73, 0x92, 0x0c, 0xf7, 0x61, + 0x53, 0x1c, 0x76, 0x48, 0xb7, 0x53, 0xc5, 0xf1, 0x3e, 0x47, 0x24, 0xc7, 0xfb, 0xab, 0x2e, 0xb2, + 0xae, 0xaf, 0xbc, 0xc8, 0xba, 0xfa, 0x82, 0xec, 0x6d, 0xa8, 0xc8, 0xd7, 0x40, 0xb9, 0x9e, 0x04, + 0x46, 0x72, 0x03, 0xf4, 0xcf, 0xd7, 0x01, 0x12, 0x7f, 0xd5, 0x2f, 0x3b, 0x3e, 0x6e, 0xc5, 0x90, + 0x64, 0x57, 0x0d, 0xc9, 0x36, 0x28, 0x32, 0x9d, 0x74, 0x1f, 0xb9, 0x9e, 0x10, 0x46, 0x6a, 0x8e, + 0x1d, 0xc8, 0x77, 0x46, 0xe9, 0xfa, 0x89, 0x08, 0x29, 0xe2, 0x48, 0xee, 0x3c, 0x17, 0x0b, 0xb0, + 0x64, 0x07, 0x5c, 0x26, 0xb3, 0x4f, 0xe1, 0x7a, 0xcc, 0xa9, 0x9f, 0xd9, 0xe1, 0x89, 0x37, 0x0f, + 0xc5, 0x62, 0x0d, 0x84, 0x94, 0xba, 0x1a, 0xe5, 0xf4, 0x94, 0xa3, 0xf9, 0x7a, 0x0d, 0xd0, 0x5a, + 0x9a, 0xcc, 0x1d, 0x47, 0x0f, 0xad, 0xf3, 0x50, 0xbc, 0x21, 0xd2, 0x48, 0xb9, 0xfa, 0xa4, 0xe1, + 0xd5, 0x4a, 0x13, 0x91, 0x50, 0xff, 0x67, 0x16, 0xf2, 0x3f, 0x9a, 0x5b, 0xfe, 0x05, 0xfb, 0x08, + 0xca, 0x41, 0x38, 0x0d, 0xe5, 0x53, 0xf7, 0xeb, 0x3c, 0x03, 0xc2, 0xd3, 0xa1, 0xb9, 0x35, 0xb5, + 0xdc, 0x90, 0xfb, 0xc0, 0x91, 0x96, 0x36, 0xa0, 0xcb, 0x90, 0x0f, 0x42, 0x6b, 0x16, 0x88, 0xd8, + 0x59, 0x9e, 0x60, 0x5b, 0x90, 0x77, 0x3d, 0xd3, 0x0a, 0xd2, 0x11, 0xb2, 0x7d, 0xd4, 0x38, 0x38, + 0x82, 0xa9, 0x50, 0x88, 0x47, 0x7c, 0xe9, 0xe4, 0x9b, 0x63, 0xe8, 0xde, 0x8d, 0x65, 0x98, 0xb6, + 0x7b, 0x1c, 0xdd, 0xef, 0x8e, 0xd3, 0xb8, 0xb5, 0x92, 0x95, 0x65, 0x1c, 0x47, 0xef, 0x42, 0x88, + 0x24, 0xdb, 0x82, 0x0a, 0xfe, 0x7c, 0xea, 0xdb, 0xa1, 0x35, 0x7c, 0x14, 0x49, 0x77, 0x09, 0x84, + 0x36, 0x92, 0x69, 0x85, 0xd6, 0x38, 0x1c, 0x7e, 0x2d, 0xe2, 0x4e, 0xcb, 0x9a, 0x04, 0x61, 0xdf, + 0x03, 0x76, 0x64, 0x8c, 0x4f, 0x8f, 0x7d, 0x8a, 0x16, 0xf9, 0x7a, 0x6e, 0xf9, 0xb6, 0x15, 0xc5, + 0x99, 0x56, 0xa4, 0x4e, 0xd1, 0x36, 0x13, 0xb2, 0x1f, 0x71, 0x2a, 0xb4, 0xee, 0xa6, 0xc6, 0x79, + 0xdb, 0x9b, 0x89, 0x30, 0x3e, 0x91, 0x62, 0x8f, 0xe0, 0x2a, 0x6e, 0x8e, 0xd1, 0x85, 0x63, 0x9c, + 0x43, 0xc2, 0xbb, 0xca, 0x1f, 0x71, 0xba, 0x74, 0x62, 0x04, 0xc9, 0x1d, 0x43, 0xee, 0x14, 0x50, + 0x7f, 0x03, 0x6a, 0xa9, 0x7e, 0x5f, 0x72, 0xec, 0x0d, 0x3b, 0xbd, 0x4e, 0x6b, 0xc4, 0x1d, 0x05, + 0xc2, 0xe1, 0xb4, 0x2e, 0x79, 0xa6, 0x72, 0x92, 0x03, 0x21, 0x4f, 0x7e, 0xad, 0x8e, 0xf6, 0xb8, + 0xa3, 0x14, 0xd4, 0x5c, 0x29, 0xab, 0x64, 0xd5, 0xbf, 0xb1, 0x0e, 0x9b, 0x23, 0xdf, 0x70, 0x03, + 0x83, 0xeb, 0x30, 0x6e, 0xe8, 0x7b, 0x0e, 0xfb, 0x1e, 0x94, 0xc2, 0xb1, 0x23, 0x4f, 0x84, 0xdb, + 0x91, 0xd8, 0x59, 0x20, 0x7d, 0x30, 0x1a, 0xf3, 0x23, 0x91, 0x62, 0xc8, 0x7f, 0xb0, 0x77, 0x20, + 0x7f, 0x64, 0x1d, 0xdb, 0xae, 0x90, 0xbc, 0x57, 0x16, 0x19, 0x77, 0x10, 0xb9, 0xb7, 0xa6, 0x71, + 0x2a, 0xf6, 0x1e, 0x14, 0xc6, 0xde, 0x34, 0xda, 0xf2, 0x92, 0xdb, 0x86, 0x52, 0x41, 0x88, 0xdd, + 0x5b, 0xd3, 0x04, 0x1d, 0xfb, 0x08, 0x4a, 0xbe, 0xe7, 0x38, 0xd8, 0xef, 0x62, 0x33, 0x6c, 0x2c, + 0xf2, 0x68, 0x02, 0xbf, 0xb7, 0xa6, 0xc5, 0xb4, 0xea, 0x03, 0x28, 0x8a, 0xca, 0x62, 0x37, 0xec, + 0x74, 0x1e, 0x77, 0x45, 0x0f, 0xb6, 0x06, 0xfb, 0xfb, 0xdd, 0x11, 0xbf, 0x4a, 0xad, 0x0d, 0x7a, + 0xbd, 0x9d, 0x66, 0xeb, 0x73, 0x65, 0x7d, 0xa7, 0x04, 0x05, 0x3e, 0x3c, 0xea, 0x6f, 0x65, 0x60, + 0x63, 0xa1, 0x01, 0xec, 0x13, 0xc8, 0x91, 0x7d, 0xc5, 0xbb, 0xe7, 0xee, 0xca, 0x56, 0x4a, 0x69, + 0xae, 0x69, 0x23, 0x87, 0xfa, 0x29, 0xd4, 0xd3, 0x70, 0xc9, 0x99, 0x54, 0x83, 0xb2, 0xd6, 0x69, + 0xb6, 0xf5, 0x41, 0xbf, 0xf7, 0x25, 0xf7, 0xcd, 0x52, 0xf2, 0xa9, 0xd6, 0x25, 0xff, 0xcf, 0xaf, + 0x83, 0xb2, 0xd8, 0x31, 0xec, 0x31, 0x5a, 0x4d, 0xd3, 0x99, 0x63, 0x91, 0x72, 0x2a, 0x0d, 0xd9, + 0xad, 0x15, 0x3d, 0x29, 0xc8, 0x78, 0x98, 0xd3, 0x38, 0x95, 0x56, 0x7f, 0x03, 0xd8, 0x72, 0x0f, + 0xfe, 0xf2, 0xb2, 0xff, 0x1f, 0x19, 0xc8, 0x1d, 0x38, 0x86, 0xcb, 0xee, 0x40, 0x9e, 0x5e, 0x2b, + 0x12, 0xf2, 0x5b, 0x5e, 0x4d, 0x38, 0x2d, 0x08, 0xc7, 0xde, 0x82, 0x6c, 0x38, 0x8e, 0x6e, 0x70, + 0x5f, 0x7b, 0xce, 0xe4, 0xdb, 0x5b, 0xd3, 0x90, 0x8a, 0x6d, 0x43, 0xd6, 0x34, 0xa3, 0x5b, 0x0f, + 0xc2, 0xa5, 0xd4, 0x36, 0x42, 0xa3, 0x6d, 0x4d, 0x6c, 0xd7, 0x16, 0xaf, 0x2b, 0x21, 0x09, 0x7b, + 0x1d, 0xb2, 0xe6, 0xd8, 0x49, 0x5f, 0x61, 0xe1, 0x4e, 0x84, 0x38, 0x43, 0x73, 0xec, 0xa0, 0xee, + 0x17, 0xfa, 0x17, 0xba, 0x3f, 0x77, 0x29, 0x5e, 0x36, 0x10, 0xf6, 0x58, 0x05, 0xb5, 0x98, 0x39, + 0x05, 0xdd, 0x06, 0xe2, 0x2a, 0xe8, 0xcc, 0xb7, 0x66, 0x86, 0x1f, 0x5b, 0x62, 0x76, 0x70, 0xc0, + 0x01, 0x3b, 0x05, 0xa0, 0x47, 0x60, 0xd5, 0xb7, 0xe9, 0x29, 0x1d, 0x54, 0xe9, 0xd5, 0xe8, 0xd7, + 0x8a, 0xf7, 0x02, 0x05, 0x46, 0xfd, 0x8b, 0x2c, 0x54, 0xa4, 0xfa, 0xb0, 0x0f, 0xa0, 0x64, 0xa6, + 0x17, 0xe2, 0xf5, 0xa5, 0x4a, 0x3f, 0x68, 0x47, 0x4b, 0xd0, 0x14, 0xd3, 0x9b, 0xce, 0xdb, 0x42, + 0xfd, 0x99, 0xe1, 0xdb, 0xfc, 0x01, 0xb5, 0x75, 0xf9, 0xe0, 0x6b, 0x68, 0x85, 0x4f, 0x22, 0xcc, + 0xde, 0x9a, 0x56, 0x0d, 0xa4, 0x34, 0xd9, 0x1d, 0xa2, 0x49, 0xd9, 0xd4, 0x43, 0x74, 0x1c, 0xb8, + 0xb7, 0xa6, 0x45, 0x78, 0x24, 0xb5, 0xce, 0xad, 0xf1, 0x3c, 0x8c, 0xec, 0x8e, 0x5a, 0xd4, 0x20, + 0x02, 0xd2, 0x6b, 0x98, 0xfc, 0x27, 0x7b, 0x88, 0xd2, 0xd6, 0x70, 0x1c, 0x8f, 0x94, 0xb5, 0xbc, + 0x7c, 0x0c, 0xd5, 0x8e, 0xe1, 0xfc, 0xf5, 0xcd, 0x28, 0xc5, 0xee, 0x41, 0xde, 0x0b, 0x4f, 0xac, + 0x48, 0x9b, 0x8f, 0x1e, 0xe5, 0x41, 0x50, 0xbb, 0xd5, 0xc3, 0x99, 0x42, 0x68, 0xf5, 0x0f, 0x32, + 0x50, 0x14, 0x3d, 0xc0, 0x36, 0xa1, 0x36, 0xec, 0x8c, 0xf4, 0x27, 0x4d, 0xad, 0xdb, 0xdc, 0xe9, + 0x75, 0xc4, 0xcd, 0x9b, 0xc7, 0x5a, 0xb3, 0x2f, 0x04, 0xa4, 0xd6, 0x79, 0x32, 0xf8, 0xbc, 0xc3, + 0x3d, 0xb4, 0xed, 0x4e, 0xff, 0x4b, 0x25, 0xcb, 0x8f, 0x23, 0x3a, 0x07, 0x4d, 0x0d, 0x65, 0x65, + 0x05, 0x8a, 0x9d, 0x2f, 0x3a, 0xad, 0x43, 0x12, 0x96, 0x75, 0x80, 0x76, 0xa7, 0xd9, 0xeb, 0x0d, + 0x5a, 0x28, 0x3c, 0x0b, 0x8c, 0x41, 0xbd, 0xa5, 0x75, 0x9a, 0xa3, 0x8e, 0xde, 0x6c, 0xb5, 0x06, + 0x87, 0xfd, 0x91, 0x52, 0xc4, 0x12, 0x9b, 0xbd, 0x51, 0x47, 0x8b, 0x41, 0xf4, 0x50, 0x5a, 0x5b, + 0x1b, 0x1c, 0xc4, 0x90, 0xf2, 0x4e, 0x19, 0x6d, 0x40, 0x1a, 0x2b, 0xf5, 0x8f, 0x36, 0xa1, 0x9e, + 0x9e, 0x9a, 0xec, 0x63, 0x28, 0x99, 0x66, 0x6a, 0x8c, 0x6f, 0xae, 0x9a, 0xc2, 0x0f, 0xda, 0x66, + 0x34, 0xcc, 0xfc, 0x07, 0x7b, 0x2d, 0x5a, 0x48, 0xeb, 0x4b, 0x0b, 0x29, 0x5a, 0x46, 0x3f, 0x80, + 0x0d, 0xf1, 0x52, 0x8c, 0x69, 0x84, 0xc6, 0x91, 0x11, 0x58, 0xe9, 0x55, 0xd2, 0x22, 0x64, 0x5b, + 0xe0, 0xf6, 0xd6, 0xb4, 0xfa, 0x38, 0x05, 0x61, 0xdf, 0x87, 0xba, 0xc1, 0xbd, 0x3f, 0x11, 0x7f, + 0x4e, 0x56, 0x43, 0x9b, 0xe4, 0x04, 0x4a, 0xd8, 0x6b, 0x86, 0x0c, 0xc0, 0x89, 0x68, 0xfa, 0xde, + 0x2c, 0x61, 0xce, 0xa7, 0x4e, 0x60, 0x7d, 0x6f, 0x26, 0xf1, 0x56, 0x4d, 0x29, 0xcd, 0x3e, 0x82, + 0xaa, 0xa8, 0x79, 0xe2, 0xea, 0x88, 0x97, 0x2c, 0xaf, 0x36, 0xa9, 0x95, 0x7b, 0x6b, 0x5a, 0x65, + 0x9c, 0x24, 0xd9, 0x23, 0xd4, 0x25, 0x13, 0x25, 0xbc, 0x28, 0xcf, 0x35, 0xaa, 0x6d, 0xc4, 0x05, + 0x46, 0x9c, 0x62, 0xef, 0x01, 0x50, 0x3d, 0x39, 0x4f, 0x29, 0x15, 0x9b, 0xe4, 0x7b, 0xb3, 0x88, + 0xa5, 0x6c, 0x46, 0x09, 0xa9, 0x7a, 0xdc, 0x51, 0x55, 0x5e, 0xae, 0x1e, 0x39, 0xab, 0x92, 0xea, + 0x71, 0x1f, 0x57, 0x5c, 0x3d, 0xce, 0x06, 0x4b, 0xd5, 0x8b, 0xb8, 0x78, 0xf5, 0x38, 0x53, 0x54, + 0x3d, 0xce, 0x53, 0x59, 0xac, 0x5e, 0xc4, 0x42, 0xd5, 0xe3, 0x1c, 0xdf, 0x5f, 0xb2, 0x1e, 0xaa, + 0xcf, 0xb5, 0x1e, 0x70, 0xd8, 0xd2, 0xf6, 0xc3, 0xf7, 0xa1, 0x1e, 0x9c, 0x78, 0x67, 0x92, 0x00, + 0xa9, 0xc9, 0xdc, 0xc3, 0x13, 0xef, 0x4c, 0x96, 0x20, 0xb5, 0x40, 0x06, 0x60, 0x6d, 0x79, 0x13, + 0xe9, 0xb4, 0xb4, 0x2e, 0xd7, 0x96, 0x5a, 0xf8, 0xc4, 0xb6, 0xce, 0xb0, 0xb6, 0x46, 0x94, 0xc0, + 0x4e, 0x49, 0xdc, 0x3e, 0x81, 0x70, 0xe4, 0xa4, 0x22, 0x7a, 0x44, 0x49, 0x10, 0x3b, 0x80, 0x02, + 0x9c, 0x5b, 0x73, 0x57, 0x66, 0x53, 0xe4, 0xb9, 0x75, 0xe8, 0xa6, 0x18, 0xab, 0x9c, 0x54, 0xb0, + 0x26, 0xab, 0x22, 0xb0, 0xbe, 0x9e, 0x5b, 0xee, 0xd8, 0x12, 0x91, 0x7b, 0xa9, 0x55, 0x31, 0x14, + 0xb8, 0x64, 0x55, 0x44, 0x90, 0x78, 0x5e, 0xc7, 0xec, 0x6c, 0x71, 0x5e, 0x4b, 0xcc, 0x34, 0xaf, + 0x63, 0xd6, 0x78, 0x41, 0xc5, 0xbc, 0x97, 0x96, 0x16, 0x94, 0xc4, 0xcc, 0x17, 0x54, 0xcc, 0xfd, + 0x08, 0xc4, 0x6c, 0xe2, 0x9d, 0x9b, 0x8a, 0xef, 0xe3, 0xb5, 0x16, 0xbd, 0x0b, 0xe3, 0x38, 0x85, + 0x73, 0xd5, 0xb7, 0xd0, 0x5a, 0x11, 0x53, 0xe1, 0x8a, 0x3c, 0x57, 0x35, 0xc2, 0xc4, 0x4b, 0xc9, + 0x4f, 0x92, 0x52, 0x61, 0x33, 0x3b, 0xf4, 0x1b, 0xe6, 0x72, 0x61, 0x07, 0x76, 0xe8, 0x27, 0x85, + 0x61, 0x8a, 0xbd, 0x03, 0x34, 0x0d, 0x39, 0x8b, 0x25, 0x8b, 0x6e, 0xec, 0x16, 0xc1, 0x50, 0x32, + 0xc5, 0x6f, 0x9c, 0x2c, 0xa2, 0x8c, 0xb1, 0x39, 0x6e, 0x4c, 0xe4, 0xc9, 0xc2, 0x8b, 0x68, 0xb5, + 0x5b, 0x38, 0x59, 0x38, 0x51, 0xcb, 0x1c, 0xb3, 0xfb, 0x40, 0xdc, 0x44, 0x7f, 0x9c, 0x7a, 0xf4, + 0xcd, 0xf7, 0x66, 0x9c, 0xba, 0x88, 0x04, 0x48, 0x8b, 0x2d, 0x70, 0x3c, 0x37, 0x6a, 0xf8, 0x49, + 0xaa, 0x05, 0x88, 0x88, 0x85, 0xc1, 0x38, 0x4e, 0xa9, 0xbf, 0x53, 0x80, 0xa2, 0x90, 0xb5, 0xec, + 0x12, 0x6c, 0x08, 0x91, 0xdf, 0x6e, 0x8e, 0x9a, 0x3b, 0xcd, 0x21, 0x2a, 0x69, 0x0c, 0xea, 0x5c, + 0xe6, 0xc7, 0xb0, 0x0c, 0xee, 0x03, 0x24, 0xf4, 0x63, 0xd0, 0x3a, 0xee, 0x03, 0x82, 0x97, 0x3f, + 0xae, 0x99, 0x65, 0x1b, 0x50, 0xe1, 0x8c, 0x1c, 0x40, 0x17, 0x81, 0x89, 0x8b, 0xa7, 0xf3, 0x12, + 0x0b, 0x3f, 0xb0, 0x2c, 0x24, 0x2c, 0x1c, 0x50, 0x8c, 0x59, 0xa2, 0x13, 0x4d, 0x06, 0xf5, 0x91, + 0x76, 0xd8, 0x6f, 0x25, 0xe5, 0x94, 0xe9, 0xf2, 0x26, 0xcf, 0xe6, 0x49, 0xb7, 0xf3, 0x54, 0x01, + 0x64, 0xe2, 0xb9, 0x50, 0xba, 0x82, 0x6a, 0x26, 0x65, 0x42, 0xc9, 0x2a, 0xbb, 0x06, 0x97, 0x86, + 0x7b, 0x83, 0xa7, 0x3a, 0x67, 0x8a, 0x9b, 0x50, 0x63, 0x97, 0x41, 0x91, 0x10, 0x3c, 0xfb, 0x3a, + 0x16, 0x49, 0xd0, 0x88, 0x70, 0xa8, 0x6c, 0xd0, 0xe1, 0x3f, 0xc2, 0x46, 0x7c, 0xdf, 0x55, 0xb0, + 0x29, 0x9c, 0x75, 0xd0, 0x3b, 0xdc, 0xef, 0x0f, 0x95, 0x4d, 0xac, 0x04, 0x41, 0x78, 0xcd, 0x59, + 0x9c, 0x4d, 0xb2, 0x5b, 0x5f, 0xa2, 0x0d, 0x1c, 0x61, 0x4f, 0x9b, 0x5a, 0xbf, 0xdb, 0x7f, 0x3c, + 0x54, 0x2e, 0xc7, 0x39, 0x77, 0x34, 0x6d, 0xa0, 0x0d, 0x95, 0x2b, 0x31, 0x60, 0x38, 0x6a, 0x8e, + 0x0e, 0x87, 0xca, 0xd5, 0xb8, 0x96, 0x07, 0xda, 0xa0, 0xd5, 0x19, 0x0e, 0x7b, 0xdd, 0xe1, 0x48, + 0xb9, 0xc6, 0xae, 0xc0, 0x66, 0x52, 0xa3, 0x88, 0xb8, 0x21, 0x55, 0x54, 0x7b, 0xdc, 0x19, 0x29, + 0xd7, 0xe3, 0x6a, 0xb4, 0x06, 0xbd, 0x5e, 0x93, 0x4e, 0xb6, 0x6f, 0x20, 0x11, 0x1d, 0xe6, 0x8b, + 0xd6, 0xbc, 0x82, 0xf5, 0x3a, 0xec, 0xcb, 0xa0, 0x9b, 0xd2, 0xd4, 0x18, 0x76, 0x7e, 0x74, 0xd8, + 0xe9, 0xb7, 0x3a, 0xca, 0xab, 0xc9, 0xd4, 0x88, 0x61, 0xb7, 0xe2, 0xa9, 0x11, 0x83, 0x6e, 0xc7, + 0x65, 0x46, 0xa0, 0xa1, 0xb2, 0x85, 0xf9, 0x89, 0x7a, 0xf4, 0xfb, 0x9d, 0xd6, 0x08, 0xdb, 0xfa, + 0x5a, 0xdc, 0x8b, 0x87, 0x07, 0x8f, 0xb5, 0x66, 0xbb, 0xa3, 0xa8, 0x08, 0xd1, 0x3a, 0xfd, 0xe6, + 0x7e, 0x34, 0xda, 0x77, 0xa4, 0xd1, 0x3e, 0xe8, 0x8e, 0x34, 0xe5, 0x6e, 0x3c, 0xba, 0x94, 0x7c, + 0x9d, 0xbd, 0x02, 0xd7, 0xe4, 0x79, 0xa8, 0x3f, 0xed, 0x8e, 0xf6, 0xc4, 0x41, 0xfc, 0x3d, 0x7e, + 0x88, 0x4c, 0xc8, 0x56, 0xbb, 0xc5, 0x23, 0x0e, 0x88, 0x17, 0x53, 0xdb, 0x3b, 0x55, 0x7a, 0x23, + 0x5d, 0x28, 0x20, 0xea, 0x67, 0xc0, 0xe4, 0xe7, 0x82, 0x45, 0x00, 0x34, 0x83, 0xdc, 0xc4, 0xf7, + 0xa6, 0xd1, 0xa3, 0x1c, 0xf8, 0x1b, 0xed, 0xef, 0xd9, 0xfc, 0x88, 0x0e, 0xb7, 0x93, 0x4b, 0xf7, + 0x32, 0x48, 0xfd, 0xbb, 0x19, 0xa8, 0xa7, 0x95, 0x0f, 0x72, 0xb8, 0x4e, 0x74, 0xd7, 0x0b, 0xf9, + 0x2b, 0x67, 0x41, 0xfc, 0x8a, 0xef, 0xa4, 0xef, 0x85, 0xf4, 0xcc, 0x19, 0xb9, 0x03, 0x62, 0x5d, + 0x82, 0xe7, 0x1a, 0xa7, 0x59, 0x17, 0x2e, 0xa5, 0x5e, 0x5c, 0x4e, 0xbd, 0x31, 0xd7, 0x88, 0x5f, + 0x4a, 0x5d, 0xa8, 0xbf, 0xc6, 0x82, 0xe5, 0x36, 0x89, 0xa7, 0x13, 0x72, 0xc9, 0xd3, 0x09, 0x7b, + 0x50, 0x4b, 0xe9, 0x3a, 0xe4, 0xc5, 0x99, 0xa4, 0x6b, 0x5a, 0xb2, 0x27, 0x2f, 0xaf, 0xa6, 0xfa, + 0xb7, 0x33, 0x50, 0x95, 0x35, 0x9f, 0xef, 0x9c, 0x13, 0xc5, 0x25, 0x89, 0xdf, 0xba, 0x6d, 0x46, + 0xaf, 0x9b, 0x45, 0xa0, 0x2e, 0x7d, 0x1b, 0x82, 0xfb, 0xae, 0x77, 0x4f, 0x87, 0x71, 0x73, 0x64, + 0x10, 0xbb, 0x05, 0x40, 0x2f, 0x0f, 0xed, 0x7e, 0x8e, 0x04, 0xe2, 0x69, 0xec, 0x04, 0xa2, 0xde, + 0x86, 0xf2, 0xee, 0x69, 0x14, 0xa6, 0x25, 0xbf, 0xf5, 0x57, 0xe6, 0x2f, 0x35, 0xa8, 0x7f, 0x98, + 0x81, 0x7a, 0xe2, 0xb3, 0xa0, 0xb0, 0x02, 0xfe, 0x52, 0x37, 0x9f, 0x0e, 0xeb, 0xe6, 0x51, 0x72, + 0xfe, 0xb9, 0x2e, 0x9f, 0x7f, 0xde, 0x11, 0x99, 0x65, 0x65, 0x91, 0x1f, 0x97, 0x25, 0xde, 0x81, + 0x78, 0x04, 0x55, 0xfc, 0xaf, 0x59, 0x13, 0xcb, 0xf7, 0x2d, 0x33, 0x7d, 0x2d, 0x25, 0x21, 0x4e, + 0x11, 0x91, 0x8d, 0x27, 0x4e, 0x56, 0x9f, 0xf3, 0x74, 0x13, 0x3d, 0x29, 0xf6, 0xdf, 0xb2, 0x50, + 0x91, 0xf4, 0xc8, 0x6f, 0x34, 0xfd, 0x6e, 0x42, 0x39, 0x79, 0x48, 0x48, 0x5c, 0xbe, 0x8f, 0x01, + 0xa9, 0xb1, 0xca, 0x2e, 0x8c, 0x55, 0x03, 0x8a, 0x3e, 0xbf, 0x2c, 0x2b, 0x5c, 0xc8, 0x51, 0x32, + 0xed, 0xac, 0xcd, 0xbf, 0xe4, 0x7c, 0xe5, 0x7d, 0xa8, 0x4a, 0x9e, 0xd6, 0xa0, 0x51, 0x90, 0x5f, + 0x93, 0x8a, 0xe9, 0x2b, 0x89, 0xd7, 0x35, 0x60, 0x57, 0xa0, 0x30, 0x39, 0xd5, 0xcd, 0x23, 0x7e, + 0x25, 0xbb, 0xac, 0xe5, 0x27, 0xa7, 0xed, 0x23, 0x3a, 0x7d, 0x9a, 0xc4, 0xaa, 0x13, 0xf7, 0x7f, + 0x95, 0x26, 0x91, 0x82, 0xb4, 0x0d, 0xc5, 0xc9, 0xa9, 0x7c, 0xb5, 0x7a, 0xa9, 0xcb, 0x0b, 0x93, + 0x53, 0xba, 0x50, 0xfd, 0x2e, 0x5c, 0x16, 0xfb, 0xb7, 0x11, 0xe8, 0xfc, 0x9d, 0x13, 0x7a, 0x60, + 0x8a, 0xbf, 0xfc, 0xb7, 0xc9, 0x71, 0xcd, 0x60, 0x48, 0x18, 0x9c, 0x71, 0x2a, 0x54, 0xa5, 0x09, + 0xc8, 0x5f, 0xe2, 0x2a, 0x6b, 0x29, 0x18, 0xfb, 0x04, 0xaa, 0x93, 0x53, 0x3e, 0xa0, 0x23, 0x6f, + 0xdf, 0x12, 0x37, 0x45, 0x2e, 0x2f, 0x0e, 0x25, 0xc5, 0x7d, 0xa4, 0x28, 0xd9, 0x55, 0x28, 0x68, + 0xc6, 0xd9, 0xf0, 0x47, 0x3d, 0x52, 0x22, 0xcb, 0x9a, 0x48, 0x7d, 0x96, 0x2b, 0xd5, 0x95, 0x0d, + 0xf5, 0x1f, 0x67, 0xa0, 0x9e, 0xd8, 0x00, 0xb8, 0x08, 0xd9, 0x7d, 0xf9, 0x89, 0xfd, 0xc6, 0xa2, + 0x99, 0x80, 0x24, 0x0f, 0x46, 0x17, 0x33, 0xfe, 0x10, 0xed, 0xaa, 0xd7, 0xd3, 0x56, 0xb9, 0xbe, + 0xb3, 0x2b, 0x1f, 0xf7, 0x7e, 0x0c, 0xd9, 0xd1, 0xc5, 0x8c, 0xfb, 0x9b, 0x70, 0x4b, 0xe4, 0xb6, + 0x29, 0xdf, 0x0c, 0x29, 0xc8, 0xe8, 0xf3, 0xce, 0x97, 0xfc, 0xb1, 0x90, 0x03, 0xad, 0xbb, 0xdf, + 0xd4, 0xbe, 0xd4, 0x11, 0x40, 0x4a, 0xc3, 0xee, 0x40, 0xeb, 0x74, 0x1f, 0xf7, 0x09, 0x90, 0x23, + 0x6f, 0x54, 0x52, 0xc5, 0xa6, 0x69, 0xee, 0x9e, 0xca, 0x0f, 0x4f, 0x65, 0x52, 0x0f, 0x4f, 0xc5, + 0x6f, 0xb4, 0xc9, 0x0f, 0x56, 0x86, 0xb1, 0x3f, 0x9e, 0xc5, 0xab, 0x30, 0x5e, 0xd2, 0xec, 0x0d, + 0xc8, 0x4d, 0x4e, 0xad, 0x8b, 0xb4, 0xa1, 0x97, 0x5e, 0x40, 0x44, 0xa0, 0xfe, 0x3c, 0x03, 0x2c, + 0x55, 0x11, 0x6e, 0x7b, 0x7c, 0xd7, 0xba, 0x7c, 0x0c, 0x0d, 0x11, 0x89, 0xc9, 0xa9, 0x24, 0x5f, + 0xbb, 0xe8, 0xd2, 0x2b, 0x5e, 0x12, 0xa1, 0x9c, 0x3c, 0xf0, 0xc6, 0xde, 0x05, 0xfe, 0x16, 0x25, + 0x85, 0x09, 0xe5, 0x9e, 0x63, 0x27, 0x6a, 0x09, 0x4d, 0xf2, 0xf8, 0xa4, 0xfc, 0xa8, 0x26, 0x77, + 0xd3, 0x6f, 0x24, 0xa3, 0x46, 0x6b, 0x5e, 0xfd, 0xbd, 0x0c, 0x5c, 0x4a, 0x4f, 0x88, 0x5f, 0xac, + 0x95, 0xe9, 0x17, 0x44, 0xb3, 0x8b, 0x2f, 0x88, 0xae, 0x9a, 0x4f, 0xb9, 0x95, 0xf3, 0xe9, 0xb7, + 0x33, 0x70, 0x59, 0xea, 0xfd, 0xc4, 0x5a, 0xfc, 0x4b, 0xaa, 0x99, 0xf4, 0x90, 0x68, 0x2e, 0xf5, + 0x90, 0xa8, 0xfa, 0xc7, 0x19, 0xb8, 0xba, 0x50, 0x13, 0xcd, 0xfa, 0x4b, 0xad, 0x4b, 0xfa, 0xc1, + 0x51, 0x3a, 0x2a, 0x88, 0x1e, 0xd4, 0xc8, 0x6c, 0x67, 0xa5, 0x07, 0x47, 0xe9, 0xfc, 0x88, 0x0e, + 0x31, 0x5f, 0x15, 0xef, 0x2e, 0xe9, 0xc1, 0x85, 0x3b, 0x16, 0x83, 0x5d, 0x26, 0xc8, 0xf0, 0xc2, + 0x1d, 0xab, 0x7f, 0x94, 0x81, 0xeb, 0x0b, 0x6d, 0x68, 0xce, 0x43, 0x4f, 0x9c, 0x05, 0xff, 0x25, + 0x35, 0xe3, 0x36, 0x54, 0xe8, 0xa4, 0x5c, 0x1c, 0x30, 0xf3, 0x6e, 0x05, 0x23, 0x29, 0x57, 0x81, + 0xac, 0x69, 0x5c, 0x88, 0x47, 0x0b, 0xf0, 0x27, 0x2e, 0xd8, 0x13, 0x6f, 0xee, 0x8b, 0xe8, 0x20, + 0xfa, 0xad, 0x7e, 0x00, 0x9b, 0x49, 0xd5, 0x5b, 0xe2, 0xdd, 0xd7, 0xdb, 0x50, 0x71, 0xad, 0x33, + 0x3d, 0x7a, 0x15, 0x56, 0xc4, 0xbc, 0xb9, 0xd6, 0x99, 0x20, 0x50, 0x77, 0x65, 0x59, 0x18, 0x7f, + 0xcd, 0xc2, 0x31, 0x53, 0x71, 0x3b, 0x9e, 0x63, 0x46, 0x28, 0xcc, 0x4d, 0x6a, 0x65, 0xd1, 0xb5, + 0xce, 0x68, 0x1e, 0x9e, 0x89, 0x7c, 0x9a, 0xa6, 0x29, 0x62, 0x17, 0x56, 0x3d, 0xd3, 0x76, 0x1d, + 0x4a, 0x33, 0x3f, 0xd5, 0x4d, 0xc5, 0x99, 0xcf, 0x8b, 0xbd, 0x2b, 0x22, 0xea, 0x9e, 0x17, 0xe7, + 0xc0, 0x63, 0xec, 0xc4, 0xd7, 0x6e, 0x72, 0xc9, 0xd7, 0x6e, 0x3e, 0x14, 0x62, 0x90, 0xec, 0x3e, + 0x5e, 0xb2, 0x02, 0x59, 0xdb, 0x3c, 0xa7, 0x82, 0x6b, 0x1a, 0xfe, 0x24, 0x4d, 0xce, 0xfa, 0x5a, + 0x04, 0xf5, 0xe1, 0x4f, 0x75, 0x07, 0x2a, 0x5a, 0xca, 0xc8, 0xad, 0x4a, 0xfe, 0xa2, 0x20, 0xfd, + 0x92, 0x55, 0xd2, 0x41, 0x5a, 0x25, 0x71, 0x17, 0x05, 0x6a, 0x20, 0x04, 0xdf, 0x13, 0xc3, 0x1f, + 0x9f, 0x18, 0x7e, 0xcf, 0x72, 0x8f, 0xc3, 0x13, 0xec, 0x72, 0xee, 0xc6, 0x95, 0xbb, 0x10, 0x38, + 0x28, 0x9a, 0x0e, 0xd8, 0x8b, 0x0e, 0x91, 0x47, 0xdf, 0xd1, 0x70, 0xad, 0x33, 0xc1, 0xff, 0x2a, + 0x00, 0xf6, 0xbf, 0x40, 0xf3, 0x23, 0xc8, 0xb2, 0xe7, 0x98, 0x1c, 0xad, 0x6e, 0x8a, 0xf6, 0x8a, + 0x47, 0x37, 0xda, 0xd6, 0x44, 0x75, 0xc4, 0xc8, 0xf3, 0x06, 0x89, 0x4e, 0xf8, 0x4e, 0xc3, 0xc8, + 0x5e, 0x83, 0x6a, 0xe4, 0x91, 0xa0, 0xc7, 0xd3, 0x78, 0xf1, 0x95, 0x08, 0xd6, 0x9f, 0x4f, 0xd5, + 0xdf, 0xcf, 0x42, 0xb5, 0xc9, 0x23, 0x7b, 0x66, 0x17, 0x83, 0x59, 0xc8, 0x7e, 0x03, 0xae, 0xd0, + 0x6b, 0x2a, 0xfc, 0x3d, 0x63, 0x0a, 0xa8, 0xa1, 0xf0, 0x7c, 0xd1, 0x89, 0xf7, 0xa5, 0x4e, 0x14, + 0x2c, 0x0f, 0x86, 0xa7, 0xf6, 0x8c, 0xdf, 0x0a, 0xe9, 0x9a, 0xe7, 0x74, 0x05, 0x83, 0xc7, 0x06, + 0xd0, 0xc3, 0x2b, 0x69, 0x04, 0xbd, 0xbd, 0x80, 0xd9, 0xcf, 0x4e, 0x45, 0xb6, 0x22, 0x6c, 0x02, + 0x81, 0x07, 0xa7, 0x9c, 0xe6, 0x3e, 0x6c, 0xf2, 0x8b, 0x60, 0xcb, 0x1b, 0xf0, 0x06, 0x47, 0x24, + 0xf3, 0x7b, 0x08, 0x9b, 0xe2, 0xf1, 0x17, 0x7a, 0xe5, 0x53, 0x1f, 0x7b, 0xb3, 0x0b, 0x71, 0xf4, + 0xf8, 0xc6, 0x73, 0xaa, 0xda, 0xe5, 0xa4, 0x08, 0xe2, 0xf5, 0xdc, 0x08, 0xd2, 0xd0, 0x1b, 0x1d, + 0xb8, 0xf6, 0x9c, 0x36, 0xbd, 0x2c, 0xbc, 0xa1, 0x24, 0x85, 0x37, 0xdc, 0xd8, 0x81, 0xcb, 0xab, + 0xca, 0xfb, 0x36, 0x79, 0xa8, 0x3f, 0xab, 0x01, 0x24, 0x33, 0x36, 0xa5, 0x8e, 0x66, 0x16, 0xd4, + 0xd1, 0x6f, 0x15, 0xd4, 0xf3, 0x01, 0xd4, 0xb1, 0xab, 0xf4, 0x84, 0x23, 0xbb, 0x92, 0xa3, 0x8a, + 0x54, 0xa3, 0xe4, 0xae, 0xeb, 0x72, 0x48, 0x44, 0x6e, 0x65, 0x48, 0xc4, 0xfb, 0x50, 0xe4, 0x07, + 0x6d, 0x81, 0xb8, 0x5e, 0x7d, 0x6d, 0x71, 0xf5, 0x3d, 0x10, 0x37, 0x4c, 0x22, 0x3a, 0xd6, 0x81, + 0x3a, 0x8a, 0x7e, 0xdf, 0x0e, 0x4f, 0xa6, 0xf2, 0x65, 0xeb, 0x5b, 0xcb, 0x9c, 0x11, 0x19, 0x7f, + 0x1b, 0xd4, 0x90, 0x93, 0x92, 0xf6, 0x1a, 0x4e, 0x85, 0xf7, 0x97, 0xb4, 0xd7, 0xa2, 0xac, 0xbd, + 0x8e, 0xa6, 0xdc, 0xe7, 0x8b, 0xda, 0xeb, 0x3b, 0x70, 0x49, 0xdc, 0x7a, 0x43, 0x06, 0xec, 0x4e, + 0xa2, 0xe7, 0x41, 0x96, 0x8a, 0x78, 0x73, 0x65, 0x4a, 0xb6, 0x1d, 0x92, 0x7f, 0x01, 0x97, 0xc7, + 0x27, 0x86, 0x7b, 0x6c, 0xe9, 0xe1, 0x91, 0xa3, 0xd3, 0x47, 0x14, 0xf4, 0xa9, 0x31, 0x13, 0x4a, + 0xf5, 0x1b, 0x4b, 0x95, 0x6d, 0x11, 0xf1, 0xe8, 0xc8, 0xa1, 0x00, 0xb4, 0x38, 0x70, 0x66, 0x73, + 0xbc, 0x08, 0x5f, 0x38, 0xbf, 0x86, 0xa5, 0xf3, 0xeb, 0x45, 0x35, 0xbb, 0xb2, 0x42, 0xcd, 0x4e, + 0x94, 0xe5, 0xaa, 0xac, 0x2c, 0xb3, 0xb7, 0xa1, 0x28, 0x2e, 0xed, 0x0a, 0xbf, 0x2f, 0x5b, 0x5e, + 0x1d, 0x5a, 0x44, 0x82, 0x25, 0x45, 0xd1, 0x14, 0xf4, 0xae, 0x43, 0x9d, 0x97, 0x24, 0xc3, 0xd8, + 0x8e, 0x70, 0x7a, 0xc6, 0xc1, 0x72, 0xc2, 0xc7, 0x7b, 0x43, 0xca, 0x38, 0xc6, 0x09, 0xbb, 0x7c, + 0x81, 0xe3, 0xc6, 0x3f, 0x2a, 0x40, 0x41, 0x04, 0xca, 0xdf, 0x87, 0x9c, 0xe9, 0x7b, 0xb3, 0x38, + 0xda, 0x7c, 0x85, 0xd6, 0x4e, 0xdf, 0xcd, 0x43, 0x05, 0xff, 0x01, 0x14, 0x0c, 0xd3, 0xd4, 0x27, + 0xa7, 0xe9, 0xf3, 0xe8, 0x05, 0x05, 0x7a, 0x6f, 0x4d, 0xcb, 0x1b, 0xa4, 0x49, 0x7f, 0x0c, 0x65, + 0xa4, 0x4f, 0xc2, 0x4f, 0x2b, 0xcb, 0x66, 0x41, 0xa4, 0xea, 0xee, 0xad, 0x69, 0x25, 0x23, 0x52, + 0x7b, 0x7f, 0x35, 0xed, 0xd9, 0xcf, 0x2d, 0x35, 0x70, 0x41, 0x4f, 0x5b, 0xf0, 0xf1, 0xff, 0x1a, + 0x70, 0x57, 0x6f, 0xbc, 0x63, 0xe7, 0xe5, 0xa3, 0xcf, 0xa5, 0xfd, 0x7d, 0x6f, 0x4d, 0xe3, 0xfb, + 0x56, 0xb4, 0xdf, 0x7f, 0x18, 0x79, 0xdd, 0xe3, 0xef, 0x0b, 0xad, 0xe8, 0x19, 0x14, 0x83, 0xb1, + 0xeb, 0x9d, 0x64, 0x22, 0xb2, 0x99, 0x66, 0x14, 0x85, 0x58, 0x5c, 0x62, 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, 0x8a, 0xda, 0xe9, 0x5b, 0xf2, 0xc9, 0xc9, 0xcd, 0x95, 0x1d, 0xa5, + 0xc5, 0x87, 0x28, 0xbc, 0xb1, 0x1a, 0xe7, 0x61, 0x3d, 0xb8, 0x2c, 0x8e, 0x18, 0xf8, 0x06, 0x1c, + 0xed, 0x99, 0xb0, 0x34, 0x5e, 0xa9, 0x1d, 0x7a, 0x6f, 0x4d, 0x63, 0xc6, 0xf2, 0xbe, 0xdd, 0x82, + 0xcd, 0xa8, 0x4a, 0xb4, 0xb3, 0x4a, 0x61, 0x53, 0x72, 0x93, 0x92, 0x7d, 0x77, 0x6f, 0x4d, 0xdb, + 0x30, 0xd2, 0x20, 0xd6, 0x85, 0x4b, 0x51, 0x26, 0xe4, 0x6a, 0x17, 0x3d, 0x53, 0x5d, 0x1a, 0x45, + 0x79, 0xaf, 0xde, 0x5b, 0xd3, 0x36, 0x8d, 0xa5, 0x0d, 0x7c, 0x3f, 0xaa, 0x8f, 0xac, 0x1c, 0xf2, + 0x95, 0x78, 0x7b, 0x65, 0x37, 0x25, 0x9a, 0x6a, 0x5c, 0xb3, 0x04, 0x94, 0xc4, 0x31, 0xdc, 0xd0, + 0xe0, 0xea, 0x6a, 0x09, 0x23, 0x6f, 0x33, 0x39, 0xbe, 0xcd, 0xa8, 0xf2, 0x36, 0xb3, 0xf8, 0x4e, + 0x8b, 0xb4, 0xe9, 0xfc, 0x10, 0x6a, 0x29, 0x11, 0xcb, 0x2a, 0x50, 0x8c, 0x5e, 0x9f, 0xa7, 0xcb, + 0x31, 0xad, 0xc1, 0xc1, 0x97, 0x4a, 0x06, 0xc1, 0xdd, 0xfe, 0x70, 0xd4, 0xec, 0x8f, 0x94, 0x75, + 0x9e, 0x38, 0xe8, 0x35, 0x5b, 0x1d, 0x25, 0xab, 0xfe, 0x71, 0x16, 0xca, 0xf1, 0x29, 0xdb, 0x77, + 0xf7, 0x86, 0xc5, 0x6e, 0xa6, 0xac, 0xec, 0x66, 0x5a, 0x30, 0xf5, 0xf8, 0x87, 0x22, 0xf8, 0xb7, + 0xbb, 0x36, 0xd2, 0x06, 0x55, 0xb0, 0x7c, 0x7d, 0x3f, 0xff, 0x0d, 0xaf, 0xef, 0xcb, 0x11, 0xe8, + 0x85, 0x74, 0x04, 0xfa, 0xc2, 0x17, 0x08, 0x8a, 0xf4, 0x36, 0xb8, 0xfc, 0x05, 0x02, 0xfa, 0xa2, + 0xe9, 0x13, 0xdb, 0x3a, 0x13, 0x21, 0xdb, 0x22, 0x95, 0xde, 0xa1, 0xe1, 0x25, 0x3b, 0xf4, 0x37, + 0x91, 0xf6, 0x0f, 0xe1, 0xf2, 0xe4, 0x34, 0x7e, 0x91, 0x3c, 0x71, 0xae, 0x54, 0xa9, 0x4a, 0x2b, + 0x71, 0xec, 0x8d, 0xf8, 0x13, 0x6c, 0x35, 0xd9, 0x0d, 0x14, 0x8f, 0x56, 0xfc, 0x4d, 0xb6, 0xff, + 0x3f, 0x03, 0x90, 0x9c, 0x3f, 0xfd, 0xc2, 0xae, 0x5c, 0xc9, 0x5b, 0x96, 0x7d, 0x81, 0xb7, 0xec, + 0x65, 0x2f, 0xd4, 0x7d, 0x0d, 0xe5, 0xf8, 0xc4, 0xf1, 0xbb, 0x4f, 0xac, 0x6f, 0x55, 0xe4, 0x6f, + 0x46, 0x6e, 0xed, 0xf8, 0xc8, 0xee, 0x17, 0xed, 0x8b, 0x54, 0xf1, 0xd9, 0x97, 0x14, 0x7f, 0xce, + 0x7d, 0xcb, 0x71, 0xe1, 0xbf, 0xe4, 0xd5, 0x24, 0x4f, 0xf4, 0x5c, 0x6a, 0xa2, 0xab, 0x73, 0xe1, + 0x20, 0xff, 0xc5, 0x8b, 0xfe, 0x56, 0x0d, 0xfe, 0xaf, 0x99, 0xc8, 0x8b, 0x1b, 0x3f, 0x22, 0xff, + 0x5c, 0xa5, 0x77, 0xb5, 0x23, 0xfa, 0xdb, 0x14, 0xf7, 0x42, 0x1f, 0x55, 0xee, 0x45, 0x3e, 0xaa, + 0x37, 0x20, 0xcf, 0xb7, 0xbb, 0xfc, 0xf3, 0xfc, 0x53, 0x1c, 0xff, 0xd2, 0x4f, 0xbd, 0xa8, 0xaa, + 0x50, 0xf2, 0x79, 0x7b, 0x2f, 0x47, 0xf9, 0x46, 0x9f, 0xa9, 0xa1, 0x1b, 0x32, 0xff, 0x17, 0x97, + 0xa8, 0xdf, 0xb5, 0x4b, 0x5e, 0xec, 0xb6, 0x50, 0xff, 0x57, 0x06, 0x6a, 0xa9, 0x08, 0x82, 0xef, + 0x50, 0xc4, 0x4a, 0xb9, 0x9c, 0xfd, 0x3f, 0x48, 0x2e, 0xa7, 0x42, 0x78, 0x4b, 0xe9, 0x10, 0x5e, + 0x14, 0x77, 0xd5, 0x94, 0x09, 0xb3, 0xca, 0xd8, 0xc9, 0xac, 0x34, 0x76, 0x6e, 0xc5, 0xdf, 0xd2, + 0xec, 0xb6, 0x79, 0xc4, 0x6c, 0x4d, 0x93, 0x20, 0xec, 0x53, 0xb8, 0x2e, 0x9c, 0x08, 0xbc, 0x7f, + 0xbc, 0x89, 0x1e, 0x7f, 0x69, 0x53, 0x18, 0xe5, 0x57, 0x39, 0x01, 0xff, 0x50, 0xcf, 0xa4, 0x19, + 0x61, 0xd5, 0x2e, 0xd4, 0x52, 0xa1, 0x19, 0xd2, 0x97, 0x7d, 0x33, 0xf2, 0x97, 0x7d, 0xd9, 0x16, + 0xe4, 0xcf, 0x4e, 0x2c, 0xdf, 0x5a, 0xf1, 0x40, 0x34, 0x47, 0xa8, 0xdf, 0x87, 0xaa, 0x1c, 0x26, + 0xc6, 0xde, 0x86, 0xbc, 0x1d, 0x5a, 0xd3, 0xc8, 0x3d, 0x72, 0x75, 0x39, 0x92, 0xac, 0x1b, 0x5a, + 0x53, 0x8d, 0x13, 0xa9, 0x7f, 0x90, 0x01, 0x65, 0x11, 0x27, 0x7d, 0x7e, 0x38, 0xf3, 0x9c, 0xcf, + 0x0f, 0xaf, 0xa7, 0x2a, 0xb9, 0xea, 0x0b, 0xc2, 0x5b, 0x91, 0x52, 0xb2, 0xe2, 0xeb, 0xb5, 0x84, + 0x60, 0xf7, 0xa0, 0xe4, 0x5b, 0xf4, 0x6d, 0x57, 0x73, 0xc5, 0xcd, 0x91, 0x18, 0xa7, 0xfe, 0x6e, + 0x06, 0x8a, 0x22, 0xa6, 0x6d, 0xa5, 0xbf, 0xea, 0x4d, 0x28, 0xf2, 0xef, 0xbc, 0x46, 0x0f, 0xd5, + 0x2d, 0x85, 0x99, 0x47, 0x78, 0x76, 0x8b, 0x47, 0xfa, 0xa5, 0xfd, 0x57, 0x07, 0x8e, 0xe1, 0x6a, + 0x04, 0x17, 0xdf, 0xdf, 0x32, 0xa6, 0xe2, 0x65, 0x03, 0xfe, 0xc2, 0x18, 0x10, 0x88, 0x1e, 0x31, + 0x50, 0x7f, 0x15, 0x8a, 0x22, 0x66, 0x6e, 0x65, 0x55, 0x5e, 0xf6, 0x8d, 0xcf, 0x2d, 0x80, 0x24, + 0x88, 0x6e, 0x55, 0x0e, 0xea, 0x7d, 0x28, 0x45, 0x71, 0x73, 0x38, 0xff, 0x92, 0xa2, 0xc5, 0x95, + 0x24, 0xb9, 0x32, 0x8e, 0xf8, 0xe8, 0x42, 0xcf, 0x1b, 0x9f, 0x92, 0xb3, 0xfc, 0x5d, 0xa0, 0xfb, + 0x59, 0xa3, 0xa5, 0xa7, 0xd8, 0xd2, 0x5f, 0xdd, 0x88, 0x89, 0xd8, 0x7d, 0x88, 0xe5, 0xe5, 0xcb, + 0x5c, 0x0b, 0x6a, 0x33, 0xba, 0xc9, 0x47, 0xb3, 0xec, 0x91, 0xf0, 0xa6, 0xf6, 0xe8, 0x29, 0xd0, + 0x8c, 0xfc, 0x38, 0x72, 0xaa, 0x4e, 0x9a, 0x44, 0xa6, 0xd6, 0xa1, 0x2a, 0x07, 0xfb, 0xa8, 0x4d, + 0xd8, 0xdc, 0xb7, 0x42, 0x03, 0xe5, 0x4f, 0xf4, 0x40, 0x15, 0x9f, 0xbf, 0xf8, 0x23, 0x3d, 0x7f, + 0x17, 0xe9, 0x34, 0x4e, 0xa4, 0xfe, 0x71, 0x0e, 0x94, 0x45, 0xdc, 0x8b, 0x6e, 0x35, 0xde, 0x86, + 0x8a, 0x47, 0xf3, 0x22, 0xf5, 0x85, 0x35, 0x0e, 0x92, 0xee, 0x03, 0xa4, 0x3e, 0xb3, 0x53, 0xb2, + 0x83, 0x3d, 0xfe, 0xa1, 0x9d, 0x6b, 0xfc, 0x0a, 0x9b, 0xe3, 0x8d, 0x69, 0x5a, 0x57, 0xe9, 0xc6, + 0x5a, 0xcf, 0x1b, 0xd3, 0x65, 0x49, 0xe1, 0x9d, 0xe0, 0x11, 0xa8, 0x55, 0xad, 0x24, 0x5c, 0x12, + 0x74, 0x7e, 0x27, 0x6e, 0x09, 0x84, 0x81, 0xb8, 0x03, 0x5c, 0xe2, 0x80, 0x51, 0x10, 0x3d, 0xed, + 0x3f, 0x16, 0x9f, 0x03, 0xcb, 0xd2, 0xd3, 0xfe, 0x2d, 0x97, 0xee, 0x4a, 0xd2, 0x83, 0xc7, 0x63, + 0xf1, 0x21, 0x44, 0xf1, 0x71, 0x05, 0x44, 0xdd, 0xe1, 0x1f, 0x4c, 0xf3, 0xad, 0x20, 0xe0, 0x2f, + 0x42, 0x96, 0xc5, 0x53, 0xa0, 0x02, 0x18, 0x3f, 0xb8, 0x2b, 0xbe, 0x65, 0x87, 0x24, 0x20, 0xde, + 0xa5, 0xe4, 0x5f, 0xb2, 0x43, 0x82, 0xeb, 0x50, 0xfa, 0x89, 0xe7, 0x5a, 0xe4, 0xe5, 0xa8, 0x50, + 0xad, 0x8a, 0x98, 0xde, 0x37, 0x66, 0xb8, 0x11, 0x38, 0x74, 0xdd, 0x9c, 0xdf, 0x12, 0xe4, 0x09, + 0xf5, 0xdf, 0x64, 0xe0, 0xf2, 0x62, 0x5f, 0xd3, 0x34, 0xaa, 0x42, 0xa9, 0x35, 0xe8, 0xe9, 0xfd, + 0xe6, 0x7e, 0x47, 0x59, 0x63, 0x1b, 0x50, 0x19, 0xec, 0x7c, 0xd6, 0x69, 0x8d, 0x38, 0x20, 0x43, + 0x6f, 0x38, 0x0c, 0xf5, 0xbd, 0x6e, 0xbb, 0xdd, 0xe9, 0x73, 0x8b, 0x62, 0xb0, 0xf3, 0x99, 0xde, + 0x1b, 0xb4, 0xf8, 0x37, 0xaf, 0xa2, 0x88, 0x87, 0xa1, 0x92, 0xa3, 0x78, 0x08, 0x0a, 0x86, 0xc7, + 0x64, 0x9e, 0x47, 0x79, 0x3f, 0x1d, 0xea, 0xad, 0xfe, 0x48, 0x29, 0x60, 0xaa, 0x7f, 0xd8, 0xeb, + 0x51, 0x8a, 0xc2, 0x39, 0x5b, 0x83, 0xfd, 0x03, 0xad, 0x33, 0x1c, 0xea, 0xc3, 0xee, 0x8f, 0x3b, + 0x4a, 0x89, 0x4a, 0xd6, 0xba, 0x8f, 0xbb, 0x7d, 0x0e, 0x28, 0xb3, 0x22, 0x64, 0xf7, 0xbb, 0x7d, + 0xfe, 0x76, 0xc5, 0x7e, 0xf3, 0x0b, 0xa5, 0x82, 0x3f, 0x86, 0x87, 0xfb, 0x4a, 0x95, 0x95, 0x21, + 0xdf, 0xeb, 0x3c, 0xe9, 0xf4, 0x94, 0x9a, 0xfa, 0x1f, 0xb2, 0x91, 0x46, 0x4c, 0x71, 0x4e, 0xdf, + 0x44, 0x0b, 0x5c, 0x75, 0xbe, 0x18, 0x77, 0x5a, 0x56, 0xea, 0xb4, 0x6f, 0xf2, 0x61, 0xe5, 0x3b, + 0x50, 0x8b, 0x83, 0x03, 0xa4, 0xe7, 0xf8, 0xab, 0x11, 0x70, 0xc5, 0xf1, 0x45, 0x61, 0xc5, 0xf1, + 0xc5, 0xcc, 0x0e, 0xd1, 0xca, 0x46, 0x99, 0xcb, 0x67, 0x52, 0x19, 0x21, 0xfc, 0x93, 0xe6, 0xaf, + 0x00, 0x25, 0xf4, 0xb9, 0x6b, 0x47, 0x9f, 0xd5, 0x2c, 0x21, 0xe0, 0xd0, 0xb5, 0xc3, 0xc5, 0xe0, + 0x84, 0xf2, 0x52, 0x70, 0x82, 0xbc, 0x39, 0x43, 0x7a, 0x73, 0x4e, 0x7f, 0x6f, 0x9a, 0x7f, 0x4f, + 0x53, 0xfa, 0xde, 0xf4, 0xdb, 0xc0, 0xc6, 0x73, 0x9f, 0x1e, 0xc7, 0x93, 0xc8, 0xaa, 0x44, 0xa6, + 0x08, 0x4c, 0xbc, 0x2b, 0xb2, 0x37, 0x60, 0x63, 0x81, 0x9a, 0x6c, 0xe9, 0xb2, 0x56, 0x4f, 0x93, + 0xb2, 0x07, 0x70, 0x49, 0xcc, 0xed, 0x54, 0xdf, 0x8a, 0xab, 0xa7, 0x1c, 0xd5, 0x4c, 0x7a, 0x58, + 0xfd, 0x15, 0x28, 0x45, 0x21, 0x6d, 0x2f, 0x56, 0x76, 0x57, 0x8c, 0xab, 0xfa, 0x0f, 0xd6, 0xa1, + 0x1c, 0x07, 0xb8, 0x7d, 0xa3, 0xd9, 0x41, 0xdf, 0x1d, 0x09, 0x4e, 0x65, 0x11, 0x53, 0x42, 0x40, + 0x34, 0x52, 0xe2, 0xb6, 0xd6, 0xdc, 0xb7, 0x23, 0x8d, 0x8d, 0x43, 0x0e, 0x7d, 0x9b, 0x5e, 0xc0, + 0xb1, 0x5d, 0xe9, 0x92, 0x68, 0x59, 0x2b, 0x21, 0x80, 0x16, 0xda, 0x75, 0xa0, 0xdf, 0xc4, 0x19, + 0x7d, 0x82, 0xdb, 0x76, 0x4f, 0x91, 0xef, 0x39, 0x9f, 0xe0, 0xa6, 0xaf, 0xa6, 0xf0, 0xe8, 0x1a, + 0x1e, 0x53, 0x10, 0x7d, 0x27, 0xf0, 0x15, 0x28, 0xcf, 0xe3, 0x0f, 0x4d, 0x8a, 0x19, 0x31, 0x8f, + 0x3e, 0x33, 0x99, 0x1e, 0xd5, 0xf2, 0xe2, 0xa8, 0x2e, 0xce, 0x69, 0x58, 0x9a, 0xd3, 0x6a, 0x08, + 0x45, 0x11, 0xe4, 0xf7, 0xe2, 0x0e, 0x7f, 0x61, 0x57, 0x29, 0x90, 0x35, 0x9c, 0xe8, 0x66, 0x2a, + 0xfe, 0x5c, 0xa8, 0x58, 0x6e, 0xa1, 0x62, 0xea, 0xdf, 0x5a, 0x07, 0x48, 0x82, 0x05, 0xd9, 0x3b, + 0x0b, 0x81, 0xc9, 0x99, 0xa5, 0x6d, 0x7f, 0x21, 0x1e, 0x79, 0xe1, 0x49, 0xa8, 0xf5, 0x6f, 0xf0, + 0x24, 0xd4, 0x43, 0xa8, 0x05, 0xfe, 0xf8, 0xa5, 0x0e, 0xf7, 0x4a, 0xe0, 0x8f, 0x63, 0x7f, 0xfb, + 0xbb, 0x80, 0x49, 0x7a, 0x2e, 0x32, 0x31, 0x54, 0x97, 0xb4, 0x96, 0x72, 0xe0, 0x8f, 0x07, 0x47, + 0x5f, 0xb5, 0xf9, 0x15, 0x39, 0x33, 0x08, 0xf5, 0x55, 0x52, 0x62, 0xc3, 0x0c, 0xc2, 0xb6, 0x2c, + 0x28, 0xee, 0x42, 0x1d, 0x69, 0x97, 0x84, 0x45, 0xd5, 0x0c, 0x92, 0x03, 0x16, 0xf5, 0x77, 0xa2, + 0x23, 0xe9, 0x05, 0x5f, 0x2e, 0xfb, 0x48, 0x18, 0xe2, 0x92, 0x12, 0xd1, 0x58, 0xe5, 0xfa, 0xe5, + 0x0f, 0x58, 0xc5, 0xa4, 0xcb, 0x9f, 0x17, 0x5c, 0xff, 0xa6, 0x9f, 0x17, 0xdc, 0x02, 0x48, 0x1e, + 0xec, 0xc4, 0x15, 0x18, 0x5f, 0xd6, 0x29, 0xf3, 0x6b, 0x38, 0xf7, 0x5f, 0x83, 0xaa, 0xfc, 0x3d, + 0x62, 0xba, 0x84, 0xe3, 0xb9, 0x16, 0xff, 0x84, 0x4b, 0xef, 0x27, 0x1f, 0x28, 0x99, 0xfb, 0x2a, + 0x54, 0xa4, 0x0f, 0x28, 0x21, 0xc5, 0x9e, 0x11, 0x9c, 0x88, 0xcf, 0x79, 0x18, 0xee, 0xb1, 0xa5, + 0x64, 0xee, 0xdf, 0x43, 0xa5, 0x5b, 0xfe, 0x7c, 0x11, 0x40, 0xa1, 0xef, 0xf9, 0x53, 0xc3, 0x11, + 0x74, 0xd6, 0x3c, 0x40, 0xba, 0x77, 0xe1, 0xca, 0xca, 0x8f, 0x31, 0xd1, 0x15, 0x2e, 0x7b, 0x3a, + 0x73, 0x2c, 0x7e, 0x19, 0x69, 0xef, 0xe2, 0xc8, 0xb7, 0x4d, 0x25, 0x73, 0xff, 0x93, 0x85, 0x8f, + 0x75, 0x1c, 0xf6, 0x77, 0x06, 0x87, 0xfd, 0x76, 0xa7, 0xcd, 0xaf, 0x09, 0x75, 0xfb, 0xad, 0xde, + 0xe1, 0xb0, 0xfb, 0x44, 0xec, 0x85, 0x9d, 0x2f, 0xa2, 0xe4, 0xfa, 0xfd, 0xbd, 0xe8, 0x35, 0x86, + 0xa8, 0xd6, 0xbd, 0x41, 0xb3, 0xcd, 0xf7, 0xd0, 0xf8, 0x3d, 0xa6, 0xd1, 0x0e, 0xff, 0xc8, 0x87, + 0xd6, 0x19, 0x1e, 0xf6, 0x46, 0xd1, 0xdb, 0x4f, 0x75, 0x80, 0x6e, 0xab, 0xb3, 0xd3, 0xd1, 0x1e, + 0x23, 0x41, 0xf6, 0xfe, 0x0f, 0xa1, 0xf1, 0xbc, 0xfb, 0x3d, 0xd8, 0xb6, 0xd6, 0x5e, 0x93, 0xee, + 0x50, 0xe1, 0x1e, 0x3a, 0xd0, 0x79, 0x8a, 0x3c, 0x7d, 0x5a, 0xa7, 0xd7, 0xa1, 0x20, 0xd8, 0xfb, + 0x3f, 0xcd, 0x48, 0xfa, 0x64, 0x74, 0x47, 0x23, 0x06, 0x88, 0x0e, 0x97, 0x41, 0x9a, 0x65, 0x98, + 0x4a, 0x86, 0x5d, 0x05, 0x96, 0x02, 0xf5, 0xbc, 0xb1, 0xe1, 0x28, 0xeb, 0x14, 0xee, 0x1a, 0xc1, + 0xe9, 0x2e, 0x9f, 0x92, 0x65, 0xaf, 0xc2, 0xf5, 0x18, 0xd6, 0xf3, 0xce, 0x0e, 0x7c, 0xdb, 0xf3, + 0xed, 0xf0, 0x82, 0xa3, 0x73, 0xf7, 0xff, 0x6f, 0x71, 0x58, 0x9b, 0x9a, 0x65, 0x58, 0x40, 0xd3, + 0x34, 0x13, 0x18, 0x09, 0x36, 0x65, 0x8d, 0x5d, 0x83, 0x4b, 0x24, 0xd5, 0x17, 0x10, 0x19, 0xf6, + 0x0a, 0x5c, 0x8b, 0x8c, 0xde, 0x45, 0xe4, 0x3a, 0x22, 0x35, 0x8b, 0x42, 0x25, 0x97, 0x90, 0xd9, + 0x9d, 0x1f, 0xfc, 0xc9, 0xcf, 0x6f, 0x65, 0xfe, 0xe5, 0xcf, 0x6f, 0x65, 0xfe, 0xe3, 0xcf, 0x6f, + 0xad, 0xfd, 0xc1, 0x7f, 0xbe, 0x95, 0xf9, 0xf1, 0x3b, 0xc7, 0x76, 0x78, 0x32, 0x3f, 0x7a, 0x30, + 0xf6, 0xa6, 0xef, 0x4e, 0x8d, 0xd0, 0xb7, 0xcf, 0xf9, 0xf6, 0x12, 0x25, 0x5c, 0xeb, 0xdd, 0xd9, + 0xe9, 0xf1, 0xbb, 0xb3, 0xa3, 0x77, 0x71, 0xa2, 0x1f, 0x15, 0x66, 0xbe, 0x17, 0x7a, 0x8f, 0xfe, + 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x74, 0xdd, 0x96, 0x1f, 0x63, 0x8a, 0x00, 0x00, } func (m *Type) Marshal() (dAtA []byte, err error) { @@ -19527,6 +19552,43 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0xca } + if len(m.TimeWindowPartitionColPos) > 0 { + dAtA76 := make([]byte, len(m.TimeWindowPartitionColPos)*10) + var j75 int + for _, num1 := range m.TimeWindowPartitionColPos { + num := uint64(num1) + for num >= 1<<7 { + dAtA76[j75] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j75++ + } + dAtA76[j75] = uint8(num) + j75++ + } + i -= j75 + copy(dAtA[i:], dAtA76[:j75]) + i = encodeVarintPlan(dAtA, i, uint64(j75)) + i-- + dAtA[i] = 0x4 + i-- + dAtA[i] = 0xd2 + } + if len(m.TimeWindowPartitionBy) > 0 { + for iNdEx := len(m.TimeWindowPartitionBy) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TimeWindowPartitionBy[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlan(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4 + i-- + dAtA[i] = 0xca + } + } if len(m.DirectView) > 0 { i -= len(m.DirectView) copy(dAtA[i:], m.DirectView) @@ -20057,21 +20119,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xe2 } if len(m.SourceStep) > 0 { - dAtA92 := make([]byte, len(m.SourceStep)*10) - var j91 int + dAtA94 := make([]byte, len(m.SourceStep)*10) + var j93 int for _, num1 := range m.SourceStep { num := uint64(num1) for num >= 1<<7 { - dAtA92[j91] = uint8(uint64(num)&0x7f | 0x80) + dAtA94[j93] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j91++ + j93++ } - dAtA92[j91] = uint8(num) - j91++ + dAtA94[j93] = uint8(num) + j93++ } - i -= j91 - copy(dAtA[i:], dAtA92[:j91]) - i = encodeVarintPlan(dAtA, i, uint64(j91)) + i -= j93 + copy(dAtA[i:], dAtA94[:j93]) + i = encodeVarintPlan(dAtA, i, uint64(j93)) i-- dAtA[i] = 0x1 i-- @@ -20108,21 +20170,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xca } if len(m.BindingTags) > 0 { - dAtA95 := make([]byte, len(m.BindingTags)*10) - var j94 int + dAtA97 := make([]byte, len(m.BindingTags)*10) + var j96 int for _, num1 := range m.BindingTags { num := uint64(num1) for num >= 1<<7 { - dAtA95[j94] = uint8(uint64(num)&0x7f | 0x80) + dAtA97[j96] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j94++ + j96++ } - dAtA95[j94] = uint8(num) - j94++ + dAtA97[j96] = uint8(num) + j96++ } - i -= j94 - copy(dAtA[i:], dAtA95[:j94]) - i = encodeVarintPlan(dAtA, i, uint64(j94)) + i -= j96 + copy(dAtA[i:], dAtA97[:j96]) + i = encodeVarintPlan(dAtA, i, uint64(j96)) i-- dAtA[i] = 0x1 i-- @@ -20372,21 +20434,21 @@ func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.Children) > 0 { - dAtA105 := make([]byte, len(m.Children)*10) - var j104 int + dAtA107 := make([]byte, len(m.Children)*10) + var j106 int for _, num1 := range m.Children { num := uint64(num1) for num >= 1<<7 { - dAtA105[j104] = uint8(uint64(num)&0x7f | 0x80) + dAtA107[j106] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j104++ + j106++ } - dAtA105[j104] = uint8(num) - j104++ + dAtA107[j106] = uint8(num) + j106++ } - i -= j104 - copy(dAtA[i:], dAtA105[:j104]) - i = encodeVarintPlan(dAtA, i, uint64(j104)) + i -= j106 + copy(dAtA[i:], dAtA107[:j106]) + i = encodeVarintPlan(dAtA, i, uint64(j106)) i-- dAtA[i] = 0x22 } @@ -20708,21 +20770,21 @@ func (m *IcebergScan) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x52 } if len(m.ProjectedFieldIds) > 0 { - dAtA112 := make([]byte, len(m.ProjectedFieldIds)*10) - var j111 int + dAtA114 := make([]byte, len(m.ProjectedFieldIds)*10) + var j113 int for _, num1 := range m.ProjectedFieldIds { num := uint64(num1) for num >= 1<<7 { - dAtA112[j111] = uint8(uint64(num)&0x7f | 0x80) + dAtA114[j113] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j111++ + j113++ } - dAtA112[j111] = uint8(num) - j111++ + dAtA114[j113] = uint8(num) + j113++ } - i -= j111 - copy(dAtA[i:], dAtA112[:j111]) - i = encodeVarintPlan(dAtA, i, uint64(j111)) + i -= j113 + copy(dAtA[i:], dAtA114[:j113]) + i = encodeVarintPlan(dAtA, i, uint64(j113)) i-- dAtA[i] = 0x4a } @@ -21016,21 +21078,21 @@ func (m *PreInsertUkCtx) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x10 } if len(m.Columns) > 0 { - dAtA119 := make([]byte, len(m.Columns)*10) - var j118 int + dAtA121 := make([]byte, len(m.Columns)*10) + var j120 int for _, num1 := range m.Columns { num := uint64(num1) for num >= 1<<7 { - dAtA119[j118] = uint8(uint64(num)&0x7f | 0x80) + dAtA121[j120] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j118++ + j120++ } - dAtA119[j118] = uint8(num) - j118++ + dAtA121[j120] = uint8(num) + j120++ } - i -= j118 - copy(dAtA[i:], dAtA119[:j118]) - i = encodeVarintPlan(dAtA, i, uint64(j118)) + i -= j120 + copy(dAtA[i:], dAtA121[:j120]) + i = encodeVarintPlan(dAtA, i, uint64(j120)) i-- dAtA[i] = 0xa } @@ -21251,21 +21313,21 @@ func (m *IdList) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.List) > 0 { - dAtA126 := make([]byte, len(m.List)*10) - var j125 int + dAtA128 := make([]byte, len(m.List)*10) + var j127 int for _, num1 := range m.List { num := uint64(num1) for num >= 1<<7 { - dAtA126[j125] = uint8(uint64(num)&0x7f | 0x80) + dAtA128[j127] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j125++ + j127++ } - dAtA126[j125] = uint8(num) - j125++ + dAtA128[j127] = uint8(num) + j127++ } - i -= j125 - copy(dAtA[i:], dAtA126[:j125]) - i = encodeVarintPlan(dAtA, i, uint64(j125)) + i -= j127 + copy(dAtA[i:], dAtA128[:j127]) + i = encodeVarintPlan(dAtA, i, uint64(j127)) i-- dAtA[i] = 0xa } @@ -21699,21 +21761,21 @@ func (m *Query) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.Steps) > 0 { - dAtA133 := make([]byte, len(m.Steps)*10) - var j132 int + dAtA135 := make([]byte, len(m.Steps)*10) + var j134 int for _, num1 := range m.Steps { num := uint64(num1) for num >= 1<<7 { - dAtA133[j132] = uint8(uint64(num)&0x7f | 0x80) + dAtA135[j134] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j132++ + j134++ } - dAtA133[j132] = uint8(num) - j132++ + dAtA135[j134] = uint8(num) + j134++ } - i -= j132 - copy(dAtA[i:], dAtA133[:j132]) - i = encodeVarintPlan(dAtA, i, uint64(j132)) + i -= j134 + copy(dAtA[i:], dAtA135[:j134]) + i = encodeVarintPlan(dAtA, i, uint64(j134)) i-- dAtA[i] = 0x12 } @@ -24676,20 +24738,20 @@ func (m *DropTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.FkChildTblsReferToMe) > 0 { - dAtA198 := make([]byte, len(m.FkChildTblsReferToMe)*10) - var j197 int + dAtA200 := make([]byte, len(m.FkChildTblsReferToMe)*10) + var j199 int for _, num := range m.FkChildTblsReferToMe { for num >= 1<<7 { - dAtA198[j197] = uint8(uint64(num)&0x7f | 0x80) + dAtA200[j199] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j197++ + j199++ } - dAtA198[j197] = uint8(num) - j197++ + dAtA200[j199] = uint8(num) + j199++ } - i -= j197 - copy(dAtA[i:], dAtA198[:j197]) - i = encodeVarintPlan(dAtA, i, uint64(j197)) + i -= j199 + copy(dAtA[i:], dAtA200[:j199]) + i = encodeVarintPlan(dAtA, i, uint64(j199)) i-- dAtA[i] = 0x62 } @@ -24725,20 +24787,20 @@ func (m *DropTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x48 } if len(m.ForeignTbl) > 0 { - dAtA201 := make([]byte, len(m.ForeignTbl)*10) - var j200 int + dAtA203 := make([]byte, len(m.ForeignTbl)*10) + var j202 int for _, num := range m.ForeignTbl { for num >= 1<<7 { - dAtA201[j200] = uint8(uint64(num)&0x7f | 0x80) + dAtA203[j202] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j200++ + j202++ } - dAtA201[j200] = uint8(num) - j200++ + dAtA203[j202] = uint8(num) + j202++ } - i -= j200 - copy(dAtA[i:], dAtA201[:j200]) - i = encodeVarintPlan(dAtA, i, uint64(j200)) + i -= j202 + copy(dAtA[i:], dAtA203[:j202]) + i = encodeVarintPlan(dAtA, i, uint64(j202)) i-- dAtA[i] = 0x3a } @@ -25284,20 +25346,20 @@ func (m *TruncateTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x40 } if len(m.ForeignTbl) > 0 { - dAtA210 := make([]byte, len(m.ForeignTbl)*10) - var j209 int + dAtA212 := make([]byte, len(m.ForeignTbl)*10) + var j211 int for _, num := range m.ForeignTbl { for num >= 1<<7 { - dAtA210[j209] = uint8(uint64(num)&0x7f | 0x80) + dAtA212[j211] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j209++ + j211++ } - dAtA210[j209] = uint8(num) - j209++ + dAtA212[j211] = uint8(num) + j211++ } - i -= j209 - copy(dAtA[i:], dAtA210[:j209]) - i = encodeVarintPlan(dAtA, i, uint64(j209)) + i -= j211 + copy(dAtA[i:], dAtA212[:j211]) + i = encodeVarintPlan(dAtA, i, uint64(j211)) i-- dAtA[i] = 0x3a } @@ -25374,20 +25436,20 @@ func (m *ClusterTable) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x18 } if len(m.AccountIDs) > 0 { - dAtA213 := make([]byte, len(m.AccountIDs)*10) - var j212 int + dAtA215 := make([]byte, len(m.AccountIDs)*10) + var j214 int for _, num := range m.AccountIDs { for num >= 1<<7 { - dAtA213[j212] = uint8(uint64(num)&0x7f | 0x80) + dAtA215[j214] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j212++ + j214++ } - dAtA213[j212] = uint8(num) - j212++ + dAtA215[j214] = uint8(num) + j214++ } - i -= j212 - copy(dAtA[i:], dAtA213[:j212]) - i = encodeVarintPlan(dAtA, i, uint64(j212)) + i -= j214 + copy(dAtA[i:], dAtA215[:j214]) + i = encodeVarintPlan(dAtA, i, uint64(j214)) i-- dAtA[i] = 0x12 } @@ -25599,21 +25661,21 @@ func (m *Prepare) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ParamTypes) > 0 { - dAtA217 := make([]byte, len(m.ParamTypes)*10) - var j216 int + dAtA219 := make([]byte, len(m.ParamTypes)*10) + var j218 int for _, num1 := range m.ParamTypes { num := uint64(num1) for num >= 1<<7 { - dAtA217[j216] = uint8(uint64(num)&0x7f | 0x80) + dAtA219[j218] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j216++ + j218++ } - dAtA217[j216] = uint8(num) - j216++ + dAtA219[j218] = uint8(num) + j218++ } - i -= j216 - copy(dAtA[i:], dAtA217[:j216]) - i = encodeVarintPlan(dAtA, i, uint64(j216)) + i -= j218 + copy(dAtA[i:], dAtA219[:j218]) + i = encodeVarintPlan(dAtA, i, uint64(j218)) i-- dAtA[i] = 0x22 } @@ -25760,21 +25822,21 @@ func (m *OtherDCL) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ParamTypes) > 0 { - dAtA220 := make([]byte, len(m.ParamTypes)*10) - var j219 int + dAtA222 := make([]byte, len(m.ParamTypes)*10) + var j221 int for _, num1 := range m.ParamTypes { num := uint64(num1) for num >= 1<<7 { - dAtA220[j219] = uint8(uint64(num)&0x7f | 0x80) + dAtA222[j221] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j219++ + j221++ } - dAtA220[j219] = uint8(num) - j219++ + dAtA222[j221] = uint8(num) + j221++ } - i -= j219 - copy(dAtA[i:], dAtA220[:j219]) - i = encodeVarintPlan(dAtA, i, uint64(j219)) + i -= j221 + copy(dAtA[i:], dAtA222[:j221]) + i = encodeVarintPlan(dAtA, i, uint64(j221)) i-- dAtA[i] = 0xa } @@ -29053,6 +29115,19 @@ func (m *Node) ProtoSize() (n int) { if l > 0 { n += 2 + l + sovPlan(uint64(l)) } + if len(m.TimeWindowPartitionBy) > 0 { + for _, e := range m.TimeWindowPartitionBy { + l = e.ProtoSize() + n += 2 + l + sovPlan(uint64(l)) + } + } + if len(m.TimeWindowPartitionColPos) > 0 { + l = 0 + for _, e := range m.TimeWindowPartitionColPos { + l += sovPlan(uint64(e)) + } + n += 2 + sovPlan(uint64(l)) + l + } if m.RankOption != nil { l = m.RankOption.ProtoSize() n += 2 + l + sovPlan(uint64(l)) @@ -46221,6 +46296,116 @@ func (m *Node) Unmarshal(dAtA []byte) error { } m.DirectView = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 73: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeWindowPartitionBy", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TimeWindowPartitionBy = append(m.TimeWindowPartitionBy, &Expr{}) + if err := m.TimeWindowPartitionBy[len(m.TimeWindowPartitionBy)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 74: + if wireType == 0 { + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.TimeWindowPartitionColPos = append(m.TimeWindowPartitionColPos, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthPlan + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthPlan + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.TimeWindowPartitionColPos) == 0 { + m.TimeWindowPartitionColPos = make([]int32, 0, elementCount) + } + for iNdEx < postIndex { + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.TimeWindowPartitionColPos = append(m.TimeWindowPartitionColPos, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field TimeWindowPartitionColPos", wireType) + } case 89: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RankOption", wireType) diff --git a/pkg/sql/colexec/fill/fill.go b/pkg/sql/colexec/fill/fill.go index 6c7f63ae9770f..99540d1dd0960 100644 --- a/pkg/sql/colexec/fill/fill.go +++ b/pkg/sql/colexec/fill/fill.go @@ -17,6 +17,7 @@ package fill import ( "bytes" "fmt" + "io" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/batch" @@ -47,6 +48,7 @@ func (fill *Fill) Prepare(proc *process.Process) (err error) { } ctr := &fill.ctr + ctr.spillThreshold = colexec.ResolveSpillThreshold(fill.SpillThreshold) switch fill.FillType { case plan.Node_VALUE: @@ -75,17 +77,26 @@ func (fill *Fill) Prepare(proc *process.Process) (err error) { case plan.Node_PREV: if len(ctr.prevVecs) == 0 { ctr.prevVecs = make([]*vector.Vector, fill.ColLen) + ctr.prevValid = make([]bool, fill.ColLen) } ctr.process = processPrev case plan.Node_NEXT: - ctr.i = 0 - ctr.idx = 0 - ctr.status = receiveBat - ctr.subStatus = findNull + if len(ctr.nextRun) < fill.ColLen { + ctr.nextRun = make([][]fillCoord, fill.ColLen) + } ctr.process = processNext case plan.Node_LINEAR: - ctr.i = 0 - ctr.idx = 0 + if len(ctr.linRun) < fill.ColLen { + ctr.linRun = make([][]fillCoord, fill.ColLen) + ctr.linPre = make([]fillCoord, fill.ColLen) + ctr.linSeed = make([]*vector.Vector, fill.ColLen) + ctr.linSeedValid = make([]bool, fill.ColLen) + ctr.linEntry = make([]*vector.Vector, fill.ColLen) + ctr.linEntryValid = make([]bool, fill.ColLen) + for i := range ctr.linPre { + ctr.linPre[i] = fillCoord{seq: -1, row: -1} + } + } if len(ctr.exes) == 0 { ctr.valVecs = make([]*vector.Vector, len(fill.FillVal)) for _, v := range fill.FillVal { @@ -166,130 +177,215 @@ func processValue(ctr *container, ap *Fill, proc *process.Process, analyzer proc return result, nil } -func (ctr *container) initIndex() { - ctr.nullIdx = 0 - ctr.nullRow = 0 - ctr.preIdx = 0 - ctr.preRow = 0 - ctr.curIdx = 0 - ctr.curRow = 0 - ctr.status = receiveBat - ctr.subStatus = findNullPre +// partKeyAt reads one partition-key cell, tolerating the constant vectors the +// time window emits when it broadcasts a key across a flushed batch. +func partKeyAt(vec *vector.Vector, row int) (val []byte, isNull bool) { + if vec.IsConstNull() { + return nil, true + } + if vec.IsConst() { + row = 0 + } + if vec.IsNull(uint64(row)) { + return nil, true + } + return vec.GetRawBytesAt(row), false } -func processNextCol(ctr *container, ap *Fill, idx int, proc *process.Process, analyzer process.Analyzer) error { - var err error - ctr.initIndex() - ctr.preIdx = ctr.doneIdx[idx] - ctr.status = findNull - for { - switch ctr.status { - case receiveBat: - if ctr.doneIdx[idx] == len(ctr.bats) || - ctr.endBatch[idx] { - return nil - } +// samePartitionRows reports whether two rows carry the same partition key. +// NULL keys compare equal, matching how GROUP BY folded them into one group. +func samePartitionRows(partIdx []int32, batA *batch.Batch, rowA int, batB *batch.Batch, rowB int) bool { + for _, col := range partIdx { + valA, nullA := partKeyAt(batA.Vecs[col], rowA) + valB, nullB := partKeyAt(batB.Vecs[col], rowB) + if nullA || nullB { + if nullA != nullB { + return false + } + continue + } + if !bytes.Equal(valA, valB) { + return false + } + } + return true +} - result, err := vm.ChildrenCall(ap.GetChildren(0), proc, analyzer) - if err != nil { - return err - } - if result.Batch == nil { - ctr.endBatch[idx] = true - return nil - } else { - if len(ctr.bats) > ctr.i { - if ctr.bats[ctr.i] != nil { - ctr.bats[ctr.i].CleanOnlyData() - } - ctr.bats[ctr.i], err = ctr.bats[ctr.i].AppendWithCopy(proc.Ctx, proc.Mp(), result.Batch) - if err != nil { - return err - } - } else { - appBat, err := result.Batch.Dup(proc.Mp()) - if err != nil { - return err - } - analyzer.Alloc(int64(appBat.Size())) - ctr.bats = append(ctr.bats, appBat) - } - ctr.i++ - } +// snapshotPartKey copies a row's partition key out of the batch, so the next +// batch can detect a boundary after this one has been recycled. +func (ctr *container) snapshotPartKey(partIdx []int32, bat *batch.Batch, row int) { + if cap(ctr.prevPartKey) < len(partIdx) { + ctr.prevPartKey = make([][]byte, len(partIdx)) + ctr.prevPartNull = make([]bool, len(partIdx)) + } + ctr.prevPartKey = ctr.prevPartKey[:len(partIdx)] + ctr.prevPartNull = ctr.prevPartNull[:len(partIdx)] + for i, col := range partIdx { + val, isNull := partKeyAt(bat.Vecs[col], row) + ctr.prevPartNull[i] = isNull + ctr.prevPartKey[i] = append(ctr.prevPartKey[i][:0], val...) + } + ctr.prevPartSet = true +} - if ctr.subStatus == findNull { - ctr.status = findNull - } else { - ctr.status = findValue +// matchesSnapshot compares a row against the saved cross-batch partition key. +func (ctr *container) matchesSnapshot(partIdx []int32, bat *batch.Batch, row int) bool { + if !ctr.prevPartSet { + return true + } + for i, col := range partIdx { + val, isNull := partKeyAt(bat.Vecs[col], row) + if isNull || ctr.prevPartNull[i] { + if isNull != ctr.prevPartNull[i] { + return false } + continue + } + if !bytes.Equal(val, ctr.prevPartKey[i]) { + return false + } + } + return true +} - case findNull: - b := ctr.bats[ctr.preIdx] - hasNull := false - for ; ctr.preRow < b.RowCount(); ctr.preRow++ { - if b.Vecs[idx].IsNull(uint64(ctr.preRow)) { - hasNull = true - break - } - } - ctr.subStatus = findNull - if hasNull { - ctr.status = findValue - } else { - ctr.preIdx++ - ctr.doneIdx[idx] = ctr.preIdx - return nil +// batAt resolves an absolute batch sequence number to its still-pending batch. +func (ctr *container) batAt(seq int) *batch.Batch { + return ctr.bats[seq-ctr.baseSeq] +} + +// pullChild appends the next child batch to the pending FIFO and returns its +// absolute sequence number, or eof when the child is drained. +func (ctr *container) pullChild(ap *Fill, proc *process.Process, analyzer process.Analyzer) (seq int, eof bool, err error) { + result, err := vm.ChildrenCall(ap.GetChildren(0), proc, analyzer) + if err != nil { + return 0, false, err + } + if result.Batch == nil { + return 0, true, nil + } + dup, err := result.Batch.Dup(proc.Mp()) + if err != nil { + return 0, false, err + } + if err = addOriginalNullMarkers(dup, ap.ColLen, proc.Mp()); err != nil { + dup.Clean(proc.Mp()) + return 0, false, err + } + if analyzer != nil { + analyzer.Alloc(int64(dup.Size())) + } + seq = ctr.baseSeq + len(ctr.bats) + ctr.bats = append(ctr.bats, dup) + ctr.pendingBytes += int64(dup.Size()) + ctr.pendingRows += int64(dup.RowCount()) + return seq, false, nil +} + +// emitResolved pops and returns the resolved prefix of the FIFO one batch at a +// time. The popped batch is handed to the caller and freed on the next Call +// (via toFree), because no unresolved coordinate can still reference it. +func (ctr *container) emitResolved(ap *Fill, proc *process.Process) (vm.CallResult, error) { + b := ctr.bats[0] + if ap.FillType == plan.Node_LINEAR && b != nil { + if err := ctr.advanceLinearEntry(ap, b, proc); err != nil { + return vm.NewCallResult(), err + } + } + ctr.bats = ctr.bats[1:] + if b != nil { + ctr.pendingBytes -= int64(b.Size()) + ctr.pendingRows -= int64(b.RowCount()) + } + ctr.baseSeq++ + ctr.flushable-- + ctr.toFree = b + stripOriginalNullMarkers(b, ap.ColLen, proc.Mp()) + result := vm.NewCallResult() + result.Batch = b + result.Status = vm.ExecNext + return result, nil +} + +// advanceLinearEntry moves the endpoint immediately before the pending FIFO +// across one emitted batch. Original-NULL markers are authoritative here: +// interpolated cells are not endpoints, while every original non-NULL cell is. +func (ctr *container) advanceLinearEntry(ap *Fill, bat *batch.Batch, proc *process.Process) error { + for row := 0; row < bat.RowCount(); row++ { + if len(ap.PartitionColIdx) > 0 { + wasSet := ctr.linEntryPart.set + if !ctr.linEntryPart.sameAndSet(ap.PartitionColIdx, bat, row) && wasSet { + clearEndpoints(ctr.linEntryValid) } - case findValue: - ctr.curIdx = ctr.preIdx - ctr.curRow = ctr.preRow - b := ctr.bats[ctr.curIdx] - hasValue := false - for ; ctr.curRow < b.RowCount(); ctr.curRow++ { - if !b.Vecs[idx].IsNull(uint64(ctr.curRow)) { - hasValue = true - break - } + } + for col := 0; col < ap.ColLen; col++ { + if originalNullAt(bat, ap.ColLen, col, row) { + continue } - ctr.subStatus = findValue - if hasValue { - ctr.status = fillValue - } else { - ctr.status = receiveBat + if err := setEndpoint(&ctr.linEntry[col], bat.Vecs[col], row, proc); err != nil { + return err } - case fillValue: - for ; ctr.preIdx < ctr.curIdx-1; ctr.preIdx++ { - for ; ctr.preRow < ctr.bats[ctr.preIdx].RowCount(); ctr.preRow++ { - vec := ctr.bats[ctr.preIdx].Vecs[idx] - if !vec.IsNull(uint64(ctr.preRow)) { - continue - } - err = setValue(vec, ctr.bats[ctr.curIdx].Vecs[idx], ctr.preRow, ctr.curRow, proc) - if err != nil { - return err - } + ctr.linEntryValid[col] = true + } + } + return nil +} - } - if ctr.preRow == ctr.bats[ctr.preRow].RowCount() { - ctr.preRow = 0 - } - } +// isNewSegment reports whether row r of the just-arrived batch opens a new +// partition: row 0 is compared against the key snapshotted from the previous +// batch, later rows against their in-batch predecessor. +func (ctr *container) isNewSegment(ap *Fill, bat *batch.Batch, r int) bool { + if len(ap.PartitionColIdx) == 0 { + return false + } + if r == 0 { + return !ctr.matchesSnapshot(ap.PartitionColIdx, bat, 0) + } + return !samePartitionRows(ap.PartitionColIdx, bat, r-1, bat, r) +} - for ; ctr.preRow < ctr.curRow; ctr.preRow++ { - vec := ctr.bats[ctr.preIdx].Vecs[idx] - if !vec.IsNull(uint64(ctr.preRow)) { - continue - } - err = setValue(vec, ctr.bats[ctr.curIdx].Vecs[idx], ctr.preRow, ctr.curRow, proc) - if err != nil { +// consumeNext folds one freshly pulled batch into the NEXT state: NULLs join +// their column's pending run, a non-NULL back-fills and clears that run, and a +// partition boundary drops the previous partition's pending NULLs (they have no +// next value and stay NULL). +func (ctr *container) consumeNext(ap *Fill, bat *batch.Batch, seq int, proc *process.Process) error { + rows := bat.RowCount() + for r := 0; r < rows; r++ { + if ctr.isNewSegment(ap, bat, r) { + for c := 0; c < ap.ColLen; c++ { + ctr.nextRun[c] = ctr.nextRun[c][:0] + } + } + for c := 0; c < ap.ColLen; c++ { + vec := bat.Vecs[c] + if vec.IsNull(uint64(r)) { + ctr.nextRun[c] = append(ctr.nextRun[c], fillCoord{seq: seq, row: r}) + continue + } + for _, cd := range ctr.nextRun[c] { + if err := setValue(ctr.batAt(cd.seq).Vecs[c], vec, cd.row, r, proc); err != nil { return err } } + ctr.nextRun[c] = ctr.nextRun[c][:0] + } + } + if len(ap.PartitionColIdx) > 0 && rows > 0 { + ctr.snapshotPartKey(ap.PartitionColIdx, bat, rows-1) + } + ctr.recomputeFlushableNext(ap) + return nil +} - ctr.status = findNull - +// recomputeFlushableNext marks every batch before the earliest column still +// awaiting a value as emittable. +func (ctr *container) recomputeFlushableNext(ap *Fill) { + minSeq := ctr.baseSeq + len(ctr.bats) + for c := 0; c < ap.ColLen; c++ { + if len(ctr.nextRun[c]) > 0 && ctr.nextRun[c][0].seq < minSeq { + minSeq = ctr.nextRun[c][0].seq } } + ctr.flushable = minSeq - ctr.baseSeq } func processPrev(ctr *container, ap *Fill, proc *process.Process, analyzer process.Analyzer) (vm.CallResult, error) { @@ -311,10 +407,32 @@ func processPrev(ctr *container, ap *Fill, proc *process.Process, analyzer proce return result, err } + if len(ctr.prevValid) < ap.ColLen { + ctr.prevValid = make([]bool, ap.ColLen) + } + + // A previous value must not leak across a partition boundary, so find + // where new partitions start before filling: row 0 against the key saved + // from the previous batch, later rows against their predecessor. + rowCount := ctr.buf.RowCount() + var newSegment []bool + if len(ap.PartitionColIdx) > 0 && rowCount > 0 { + newSegment = make([]bool, rowCount) + newSegment[0] = !ctr.matchesSnapshot(ap.PartitionColIdx, ctr.buf, 0) + for j := 1; j < rowCount; j++ { + newSegment[j] = !samePartitionRows(ap.PartitionColIdx, ctr.buf, j-1, ctr.buf, j) + } + } + for i := 0; i < ap.ColLen; i++ { for j := 0; j < ctr.buf.Vecs[i].Length(); j++ { + if newSegment != nil && newSegment[j] { + // The new partition has no previous value yet. The vector is + // kept for reuse; only its validity is dropped. + ctr.prevValid[i] = false + } if ctr.buf.Vecs[i].IsNull(uint64(j)) { - if ctr.prevVecs[i] != nil { + if ctr.prevVecs[i] != nil && ctr.prevValid[i] { if err = setValue(ctr.buf.Vecs[i], ctr.prevVecs[i], j, 0, proc); err != nil { return result, err } @@ -331,172 +449,119 @@ func processPrev(ctr *container, ap *Fill, proc *process.Process, analyzer proce return result, err } } - + ctr.prevValid[i] = true } } } + if len(ap.PartitionColIdx) > 0 && rowCount > 0 { + ctr.snapshotPartKey(ap.PartitionColIdx, ctr.buf, rowCount-1) + } result.Batch = ctr.buf return result, nil } -func processLinearCol(ctr *container, ap *Fill, proc *process.Process, idx int, analyzer process.Analyzer) error { - var err error - - ctr.initIndex() - ctr.status = findNullPre - ctr.preIdx = ctr.doneIdx[idx] - - for { - switch ctr.status { - case receiveBat: - if ctr.doneIdx[idx] == len(ctr.bats) || - ctr.endBatch[idx] { - return nil +// consumeLinear folds one freshly pulled batch into the LINEAR state. A NULL +// with a known previous value of the same partition joins that column's run; a +// non-NULL interpolates the pending run against the midpoint of linPre and +// itself, then becomes the new linPre. A run whose neighbour lies across a +// partition boundary stays NULL, because there is nothing to interpolate +// between. +func (ctr *container) consumeLinear(ap *Fill, bat *batch.Batch, seq int, proc *process.Process) error { + rows := bat.RowCount() + for r := 0; r < rows; r++ { + if ctr.isNewSegment(ap, bat, r) { + ctr.clearLinearSeeds(proc.Mp()) + for c := 0; c < ap.ColLen; c++ { + ctr.linRun[c] = ctr.linRun[c][:0] + ctr.linPre[c] = fillCoord{seq: -1, row: -1} } - - result, err := vm.ChildrenCall(ap.GetChildren(0), proc, analyzer) - if err != nil { - return err - } - if result.Batch == nil { - ctr.endBatch[idx] = true - return nil - } else { - if len(ctr.bats) > ctr.i { - if ctr.bats[ctr.i] != nil { - ctr.bats[ctr.i].CleanOnlyData() - } - ctr.bats[ctr.i], err = ctr.bats[ctr.i].AppendWithCopy(proc.Ctx, proc.Mp(), result.Batch) - if err != nil { - return err - } - } else { - appBat, err := result.Batch.Dup(proc.Mp()) - if err != nil { - return err - } - analyzer.Alloc(int64(appBat.Size())) - ctr.bats = append(ctr.bats, appBat) - } - } - - if ctr.subStatus == findNullPre { - ctr.status = findNullPre - } else { - ctr.status = findValue - } - case findNullPre: - b := ctr.bats[ctr.nullIdx] - - if ctr.nullIdx > ctr.preRow { - if b.Vecs[idx].IsNull(uint64(ctr.nullIdx)) { - ctr.status = findValue - continue - } - } - - hasNullPre := false - ctr.preRow = ctr.nullRow - 1 - for ; ctr.nullRow < b.RowCount(); ctr.nullRow++ { - if ctr.preRow >= 0 && b.Vecs[idx].IsNull(uint64(ctr.nullRow)) && !b.Vecs[idx].IsNull(uint64(ctr.preRow)) { - hasNullPre = true - break - } - ctr.preRow++ - } - - ctr.subStatus = findNullPre - if hasNullPre { - ctr.status = findValue - } else { - ctr.nullIdx++ - ctr.nullRow = 0 - ctr.doneIdx[idx] = ctr.nullIdx - if b.Vecs[idx].IsNull(uint64(ctr.preRow)) { - ctr.preIdx++ - ctr.nullRow = 0 - ctr.doneIdx[idx] = ctr.preIdx + } + for c := 0; c < ap.ColLen; c++ { + vec := bat.Vecs[c] + if vec.IsNull(uint64(r)) { + seedValid := c < len(ctr.linSeedValid) && ctr.linSeedValid[c] + if ctr.linPre[c].seq >= 0 || seedValid { + ctr.linRun[c] = append(ctr.linRun[c], fillCoord{seq: seq, row: r}) } - return nil + continue } - case findValue: - ctr.curIdx = ctr.nullIdx - ctr.curRow = ctr.nullRow - b := ctr.bats[ctr.curIdx] - hasValue := false - for ; ctr.curRow < b.RowCount(); ctr.curRow++ { - if !b.Vecs[idx].IsNull(uint64(ctr.curRow)) { - hasValue = true - break + if len(ctr.linRun[c]) > 0 { + if err := ctr.interpolateRun(c, ctr.linPre[c], seq, r, proc); err != nil { + return err } + ctr.linRun[c] = ctr.linRun[c][:0] } - ctr.subStatus = findValue - if hasValue { - ctr.status = fillValue - } else { - ctr.status = receiveBat + if c < len(ctr.linSeed) && ctr.linSeed[c] != nil { + ctr.linSeed[c].Free(proc.Mp()) + ctr.linSeed[c] = nil } - case fillValue: - var valVec *vector.Vector - var owned bool - valVec, owned, err = linearFillValue(ctr, proc, idx) - if err != nil { - return err - } - - ctr.preRow++ - for ; ctr.preIdx < ctr.curIdx-1; ctr.preIdx++ { - for ; ctr.preRow < ctr.bats[ctr.preIdx].RowCount(); ctr.preRow++ { - - vec := ctr.bats[ctr.preIdx].Vecs[idx] - if !vec.IsNull(uint64(ctr.preRow)) { - continue - } - err = setValue(vec, valVec, ctr.preRow, 0, proc) - if err != nil { - if owned { - valVec.Free(proc.Mp()) - } - return err - } - - } - if ctr.preRow == ctr.bats[ctr.preRow].RowCount() { - ctr.preRow = 0 - } + if c < len(ctr.linSeedValid) { + ctr.linSeedValid[c] = false } + ctr.linPre[c] = fillCoord{seq: seq, row: r} + } + } + if len(ap.PartitionColIdx) > 0 && rows > 0 { + ctr.snapshotPartKey(ap.PartitionColIdx, bat, rows-1) + } + ctr.recomputeFlushableLinear(ap) + return nil +} - for ; ctr.preRow < ctr.curRow; ctr.preRow++ { - vec := ctr.bats[ctr.preIdx].Vecs[idx] - if !vec.IsNull(uint64(ctr.preRow)) { - continue - } - err = setValue(vec, valVec, ctr.preRow, 0, proc) - if err != nil { - if owned { - valVec.Free(proc.Mp()) - } - return err - } - } +// interpolateRun writes the midpoint of the pre and cur values into every row +// of column col's pending run. +func (ctr *container) interpolateRun(col int, pre fillCoord, curSeq, curRow int, proc *process.Process) error { + var preBatch *batch.Batch + preRow := pre.row + if pre.seq >= 0 { + preBatch = ctr.batAt(pre.seq) + } else { + preBatch = batch.NewWithSize(col + 1) + preBatch.SetVector(int32(col), ctr.linSeed[col]) + preBatch.SetRowCount(1) + preRow = 0 + } + valVec, owned, err := linearFillValue(ctr, proc, col, preBatch, preRow, ctr.batAt(curSeq), curRow) + if err != nil { + return err + } + for _, cd := range ctr.linRun[col] { + if err = setValue(ctr.batAt(cd.seq).Vecs[col], valVec, cd.row, 0, proc); err != nil { if owned { valVec.Free(proc.Mp()) } + return err + } + } + if owned { + valVec.Free(proc.Mp()) + } + return nil +} - ctr.nullIdx = ctr.preIdx - ctr.nullRow = ctr.preRow - ctr.status = findNullPre +// recomputeFlushableLinear marks batches as emittable up to the earliest one +// still needed: a pending run, or a linPre that a later non-NULL might yet pair +// with. linPre therefore pins its batch until the next non-NULL supersedes it. +func (ctr *container) recomputeFlushableLinear(ap *Fill) { + minSeq := ctr.baseSeq + len(ctr.bats) + for c := 0; c < ap.ColLen; c++ { + if ctr.linPre[c].seq >= 0 && ctr.linPre[c].seq < minSeq { + minSeq = ctr.linPre[c].seq + } + if len(ctr.linRun[c]) > 0 && ctr.linRun[c][0].seq < minSeq { + minSeq = ctr.linRun[c][0].seq } } + ctr.flushable = minSeq - ctr.baseSeq } -func linearFillValue(ctr *container, proc *process.Process, idx int) (*vector.Vector, bool, error) { - preVec := ctr.bats[ctr.preIdx].Vecs[idx] - curVec := ctr.bats[ctr.curIdx].Vecs[idx] +func linearFillValue(ctr *container, proc *process.Process, idx int, preBatch *batch.Batch, preRow int, curBatch *batch.Batch, curRow int) (*vector.Vector, bool, error) { + preVec := preBatch.Vecs[idx] + curVec := curBatch.Vecs[idx] if preVec.GetType().Oid == types.T_decimal128 && curVec.GetType().Oid == types.T_decimal128 { result := vector.NewVec(*preVec.GetType()) - left := vector.GetFixedAtNoTypeCheck[types.Decimal128](preVec, ctr.preRow) - right := vector.GetFixedAtNoTypeCheck[types.Decimal128](curVec, ctr.curRow) + left := vector.GetFixedAtNoTypeCheck[types.Decimal128](preVec, preRow) + right := vector.GetFixedAtNoTypeCheck[types.Decimal128](curVec, curRow) value, err := decimal128LinearMidpoint(left, right, preVec.GetType().Scale) if err != nil { result.Free(proc.Mp()) @@ -511,16 +576,17 @@ func linearFillValue(ctr *container, proc *process.Process, idx int) (*vector.Ve b := batch.NewWithSize(2) b.Vecs[0] = vector.NewVec(*preVec.GetType()) - if err := appendValue(b.Vecs[0], preVec, ctr.preRow, proc); err != nil { + if err := appendValue(b.Vecs[0], preVec, preRow, proc); err != nil { b.Clean(proc.Mp()) return nil, false, err } b.Vecs[1] = vector.NewVec(*curVec.GetType()) - if err := appendValue(b.Vecs[1], curVec, ctr.curRow, proc); err != nil { + if err := appendValue(b.Vecs[1], curVec, curRow, proc); err != nil { b.Clean(proc.Mp()) return nil, false, err } b.SetRowCount(1) + defer b.Clean(proc.Mp()) result, err := ctr.exes[idx].Eval(proc, []*batch.Batch{b}, nil) return result, false, err } @@ -538,123 +604,99 @@ func decimal128LinearMidpoint(left, right types.Decimal128, scale int32) (types. } func processNext(ctr *container, ap *Fill, proc *process.Process, analyzer process.Analyzer) (vm.CallResult, error) { - var err error - result := vm.NewCallResult() - if ctr.done { - if ctr.idx == len(ctr.bats) { - result.Batch = nil - result.Status = vm.ExecStop - return result, nil - } - result.Batch = ctr.bats[ctr.idx] - result.Status = vm.ExecNext - ctr.idx++ - return result, nil - } - for ; ctr.i < 1; ctr.i++ { - result, err = vm.ChildrenCall(ap.GetChildren(0), proc, analyzer) - if err != nil { - return result, err - } - if result.Batch == nil { - break - } - if len(ctr.bats) > ctr.i { - if ctr.bats[ctr.i] != nil { - ctr.bats[ctr.i].CleanOnlyData() - } - ctr.bats[ctr.i], err = ctr.bats[ctr.i].AppendWithCopy(proc.Ctx, proc.Mp(), result.Batch) - if err != nil { - return result, err - } - } else { - appBat, err := result.Batch.Dup(proc.Mp()) - if err != nil { - return result, err - } - analyzer.Alloc(int64(appBat.Size())) - ctr.bats = append(ctr.bats, appBat) - } - ctr.doneIdx = make([]int, ctr.bats[0].VectorCount()) - ctr.endBatch = make([]bool, ctr.bats[0].VectorCount()) - } - if len(ctr.bats) == 0 { - result.Batch = nil - result.Status = vm.ExecStop - return result, nil - } - - for i := range ctr.bats[0].Vecs { - if err = processNextCol(ctr, ap, i, proc, analyzer); err != nil { - return result, err - } - } - ctr.done = true - result.Batch = ctr.bats[ctr.idx] - result.Status = vm.ExecNext - ctr.idx++ - - return result, nil + return ctr.driveFill(ap, proc, analyzer, (*container).consumeNext, (*container).flushPendingRunsNext) } func processLinear(ctr *container, ap *Fill, proc *process.Process, analyzer process.Analyzer) (vm.CallResult, error) { - var err error - result := vm.NewCallResult() - if ctr.done { - if ctr.idx == len(ctr.bats) { - result.Batch = nil - result.Status = vm.ExecStop - return result, nil + return ctr.driveFill(ap, proc, analyzer, (*container).consumeLinear, (*container).flushPendingRunsLinear) +} + +// driveFill is the shared incremental loop for NEXT and LINEAR. It emits the +// resolved batch prefix as soon as it exists — only ever buffering batches that +// still contain a row whose value depends on a not-yet-seen input — and calls +// the child again only when nothing is emittable, so a no-NULL stream flows +// through without ever materializing more than the batch in flight. +func (ctr *container) driveFill( + ap *Fill, proc *process.Process, analyzer process.Analyzer, + consume func(*container, *Fill, *batch.Batch, int, *process.Process) error, + flushPendingRuns func(*container, *Fill), +) (vm.CallResult, error) { + // The batch returned last Call has been consumed by the parent by now. + if ctr.toFree != nil { + ctr.toFree.Clean(proc.Mp()) + ctr.toFree = nil + } + if ctr.spill != nil { + if !ctr.spill.ready { + if err := ctr.collectSpill(ap, proc, analyzer); err != nil { + ctr.cleanupSpill(proc) + return vm.NewCallResult(), err + } + } + bat, err := ctr.spill.replayNext(ctr, ap, proc) + if err == io.EOF { + if err = ctr.finishSpillReplay(ap, proc); err != nil { + return vm.NewCallResult(), err + } + return ctr.driveFill(ap, proc, analyzer, consume, flushPendingRuns) } - result.Batch = ctr.bats[ctr.idx] + if err != nil { + ctr.cleanupSpill(proc) + return vm.NewCallResult(), err + } + result := vm.NewCallResult() + result.Batch = bat result.Status = vm.ExecNext - ctr.idx++ return result, nil } - for ; ctr.i < 1; ctr.i++ { - result, err = vm.ChildrenCall(ap.GetChildren(0), proc, analyzer) + for { + if ctr.flushable > 0 { + return ctr.emitResolved(ap, proc) + } + if ctr.childDone { + if len(ctr.bats) == 0 { + result := vm.NewCallResult() + result.Batch = nil + result.Status = vm.ExecStop + return result, nil + } + // End of input: whatever is still pending has no future value to + // wait for, so it stays NULL and the whole tail becomes emittable. + flushPendingRuns(ctr, ap) + ctr.flushable = len(ctr.bats) + continue + } + seq, eof, err := ctr.pullChild(ap, proc, analyzer) if err != nil { - return result, err + return vm.NewCallResult(), err } - if result.Batch == nil { - break + if eof { + ctr.childDone = true + continue } - - if len(ctr.bats) > ctr.i { - if ctr.bats[ctr.i] != nil { - ctr.bats[ctr.i].CleanOnlyData() - } - ctr.bats[ctr.i], err = ctr.bats[ctr.i].AppendWithCopy(proc.Ctx, proc.Mp(), result.Batch) - if err != nil { - return result, err - } - } else { - appBat, err := result.Batch.Dup(proc.Mp()) - if err != nil { - return result, err + if err = consume(ctr, ap, ctr.batAt(seq), seq, proc); err != nil { + return vm.NewCallResult(), err + } + if ctr.flushable == 0 && ctr.shouldSpillPending() { + if err = ctr.beginSpill(ap, proc); err != nil { + return vm.NewCallResult(), err } - analyzer.Alloc(int64(appBat.Size())) - ctr.bats = append(ctr.bats, appBat) + return ctr.driveFill(ap, proc, analyzer, consume, flushPendingRuns) } - ctr.doneIdx = make([]int, ctr.bats[0].VectorCount()) - ctr.endBatch = make([]bool, ctr.bats[0].VectorCount()) } - if len(ctr.bats) == 0 { - result.Batch = nil - result.Status = vm.ExecStop - return result, nil - } - for i := range ctr.bats[0].Vecs { - if err = processLinearCol(ctr, ap, proc, i, analyzer); err != nil { - return result, err - } +} + +func (ctr *container) flushPendingRunsNext(ap *Fill) { + for c := 0; c < ap.ColLen; c++ { + ctr.nextRun[c] = ctr.nextRun[c][:0] } - ctr.done = true - result.Batch = ctr.bats[ctr.idx] - result.Status = vm.ExecNext - ctr.idx++ +} - return result, nil +func (ctr *container) flushPendingRunsLinear(ap *Fill) { + for c := 0; c < ap.ColLen; c++ { + ctr.linRun[c] = ctr.linRun[c][:0] + ctr.linPre[c] = fillCoord{seq: -1, row: -1} + } } func processDefault(ctr *container, ap *Fill, proc *process.Process, analyzer process.Analyzer) (vm.CallResult, error) { diff --git a/pkg/sql/colexec/fill/fill_test.go b/pkg/sql/colexec/fill/fill_test.go index 28d50f4cd96a6..e0032057b3234 100644 --- a/pkg/sql/colexec/fill/fill_test.go +++ b/pkg/sql/colexec/fill/fill_test.go @@ -18,8 +18,6 @@ import ( "bytes" "testing" - "github.com/matrixorigin/matrixone/pkg/sql/plan/function" - "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -43,7 +41,6 @@ func makeTestCases(t *testing.T) []fillTestCase { { proc: testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()), arg: &Fill{ - AggIds: []int32{function.MAX}, FillType: plan.Node_VALUE, FillVal: []*plan.Expr{ { @@ -70,7 +67,6 @@ func makeTestCases(t *testing.T) []fillTestCase { { proc: testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()), arg: &Fill{ - AggIds: []int32{function.MAX}, FillType: plan.Node_PREV, FillVal: []*plan.Expr{ { @@ -97,7 +93,6 @@ func makeTestCases(t *testing.T) []fillTestCase { { proc: testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()), arg: &Fill{ - AggIds: []int32{function.MAX}, FillType: plan.Node_NONE, FillVal: []*plan.Expr{ { @@ -125,7 +120,6 @@ func makeTestCases(t *testing.T) []fillTestCase { { proc: testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()), arg: &Fill{ - AggIds: []int32{function.MAX}, FillType: plan.Node_NEXT, FillVal: []*plan.Expr{ { @@ -152,7 +146,6 @@ func makeTestCases(t *testing.T) []fillTestCase { { proc: testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()), arg: &Fill{ - AggIds: []int32{function.MAX}, FillType: plan.Node_LINEAR, FillVal: []*plan.Expr{ { @@ -234,10 +227,10 @@ func TestProcessLinearDecimal128(t *testing.T) { arg := &Fill{ColLen: 1, FillType: plan.Node_LINEAR} ctr := &arg.ctr ctr.bats = []*batch.Batch{bat} - ctr.doneIdx = make([]int, 1) - ctr.endBatch = make([]bool, 1) + ctr.linRun = make([][]fillCoord, 1) + ctr.linPre = []fillCoord{{seq: -1, row: -1}} - require.NoError(t, processLinearCol(ctr, arg, proc, 0, nil)) + require.NoError(t, ctr.consumeLinear(arg, bat, 0, proc)) require.False(t, vec.IsNull(1)) require.Equal(t, types.Decimal128FromInt64(200), vector.GetFixedAtNoTypeCheck[types.Decimal128](vec, 1)) } @@ -1151,17 +1144,13 @@ func TestLinearFillValueUsesExpressionForNonDecimal128(t *testing.T) { defer resultVec.Free(proc.Mp()) ctr := &container{ - bats: []*batch.Batch{input}, - preIdx: 0, - preRow: 0, - curIdx: 0, - curRow: 2, + bats: []*batch.Batch{input}, exes: []colexec.ExpressionExecutor{ &fillStubExpressionExecutor{result: resultVec}, }, } - vec, owned, err := linearFillValue(ctr, proc, 0) + vec, owned, err := linearFillValue(ctr, proc, 0, input, 0, input, 2) require.NoError(t, err) require.False(t, owned) require.Same(t, resultVec, vec) diff --git a/pkg/sql/colexec/fill/partition_test.go b/pkg/sql/colexec/fill/partition_test.go new file mode 100644 index 0000000000000..d84e7669e18d2 --- /dev/null +++ b/pkg/sql/colexec/fill/partition_test.go @@ -0,0 +1,206 @@ +// Copyright 2021 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fill + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "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/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/colexec" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/vm" + "github.com/matrixorigin/matrixone/pkg/vm/process" + "github.com/stretchr/testify/require" +) + +// partitionedBatch builds one input batch shaped (val int64 nullable, part int64). +// nullRows lists the val rows that are NULL. +func partitionedBatch(mp *mpool.MPool, vals []int64, nullRows []uint64, parts []int64) *batch.Batch { + bat := batch.NewWithSize(2) + bat.SetVector(0, testutil.MakeInt64Vector(vals, nullRows, mp)) + bat.SetVector(1, testutil.MakeInt64Vector(parts, nil, mp)) + bat.SetRowCount(len(vals)) + return bat +} + +// runFill drives the operator to exhaustion and returns the val column as +// (values, isNull) per row across every output batch. +func runFill(t *testing.T, proc *process.Process, fillType plan.Node_FillType, bats []*batch.Batch) (vals []int64, nulls []bool) { + t.Helper() + arg := &Fill{ + ColLen: 1, + FillType: fillType, + PartitionColIdx: []int32{1}, + } + arg.AppendChild(colexec.NewMockOperator().WithBatchs(bats)) + require.NoError(t, arg.Prepare(proc)) + defer arg.Free(proc, false, nil) + + for { + res, err := arg.Call(proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + vec := res.Batch.Vecs[0] + for r := 0; r < res.Batch.RowCount(); r++ { + if vec.IsNull(uint64(r)) { + nulls = append(nulls, true) + vals = append(vals, 0) + } else { + nulls = append(nulls, false) + vals = append(vals, vector.GetFixedAtNoTypeCheck[int64](vec, r)) + } + } + if res.Status == vm.ExecStop { + break + } + } + return +} + +// fill(prev) must not carry the last value of one partition into the next: +// the first window of a new partition has no previous value. +func TestFillPrevStopsAtPartitionBoundary(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + defer proc.Free() + + // batch 1: partition 1 = [10, NULL]; batch 2: partition 2 = [NULL, 30] + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10, 0}, []uint64{1}, []int64{1, 1}), + partitionedBatch(proc.Mp(), []int64{0, 30}, []uint64{0}, []int64{2, 2}), + } + defer func() { + for _, b := range bats { + b.Clean(proc.Mp()) + } + }() + + vals, nulls := runFill(t, proc, plan.Node_PREV, bats) + require.Equal(t, []bool{false, false, true, false}, nulls, + "partition 2's leading NULL has no previous value and must stay NULL") + require.Equal(t, int64(10), vals[1], "within partition 1 the previous value fills") + require.Equal(t, int64(30), vals[3]) +} + +// The boundary must also hold inside a single batch: the non-sliding window +// forwards rows of several partitions in one batch. +func TestFillPrevPartitionBoundaryWithinBatch(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + defer proc.Free() + + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10, 0, 0, 30}, []uint64{1, 2}, []int64{1, 1, 2, 2}), + } + defer bats[0].Clean(proc.Mp()) + + vals, nulls := runFill(t, proc, plan.Node_PREV, bats) + require.Equal(t, []bool{false, false, true, false}, nulls) + require.Equal(t, int64(10), vals[1]) +} + +// fill(next) is the mirror image: the trailing NULLs of a partition have no +// next value, because the following rows belong to another group. +func TestFillNextStopsAtPartitionBoundary(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + defer proc.Free() + + // partition 1 = [NULL, 10, NULL]; partition 2 = [20, NULL] + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{0, 10, 0}, []uint64{0, 2}, []int64{1, 1, 1}), + partitionedBatch(proc.Mp(), []int64{20, 0}, []uint64{1}, []int64{2, 2}), + } + defer func() { + for _, b := range bats { + b.Clean(proc.Mp()) + } + }() + + vals, nulls := runFill(t, proc, plan.Node_NEXT, bats) + require.Equal(t, []bool{false, false, true, false, true}, nulls, + "trailing NULLs of each partition must stay NULL") + require.Equal(t, int64(10), vals[0], "within partition 1 the next value fills backwards") +} + +// A batch containing no NULL must not end the scan: batches after it still +// need their NULLs filled. The old streaming state machine dropped them. +func TestFillNextConsumesBatchesAfterNullFreeBatch(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + defer proc.Free() + + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{1, 2}, nil, []int64{1, 1}), + partitionedBatch(proc.Mp(), []int64{0, 9}, []uint64{0}, []int64{1, 1}), + } + defer func() { + for _, b := range bats { + b.Clean(proc.Mp()) + } + }() + + vals, nulls := runFill(t, proc, plan.Node_NEXT, bats) + require.Len(t, vals, 4, "every input row must be emitted") + require.Equal(t, []bool{false, false, false, false}, nulls) + require.Equal(t, int64(9), vals[2]) +} + +// fill(linear) needs a neighbour on both sides inside the same partition. +// decimal128 exercises the built-in midpoint path, which needs no expression +// executor. +func TestFillLinearStopsAtPartitionBoundary(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + defer proc.Free() + + // One batch, two partitions: [10, NULL, 30, NULL | NULL, 100]. + // The NULL at the tail of partition 1 has no following value in its + // partition, and partition 2's leading NULL has no previous one: without + // the boundary, linear would interpolate 30 and 100 across the groups. + typ := types.New(types.T_decimal128, 38, 0) + valVec := vector.NewVec(typ) + set := func(v int64, isNull bool) { + require.NoError(t, vector.AppendFixed(valVec, types.Decimal128FromInt64(v), isNull, proc.Mp())) + } + set(10, false) + set(0, true) + set(30, false) + set(0, true) + set(0, true) + set(100, false) + + bat := batch.NewWithSize(2) + bat.SetVector(0, valVec) + bat.SetVector(1, testutil.MakeInt64Vector([]int64{1, 1, 1, 1, 2, 2}, nil, proc.Mp())) + bat.SetRowCount(6) + defer bat.Clean(proc.Mp()) + + arg := &Fill{ColLen: 1, FillType: plan.Node_LINEAR, PartitionColIdx: []int32{1}} + ctr := &arg.ctr + ctr.bats = []*batch.Batch{bat} + ctr.linRun = make([][]fillCoord, 1) + ctr.linPre = []fillCoord{{seq: -1, row: -1}} + + require.NoError(t, ctr.consumeLinear(arg, bat, 0, proc)) + + require.False(t, valVec.IsNull(1)) + require.Equal(t, types.Decimal128FromInt64(20), + vector.GetFixedAtNoTypeCheck[types.Decimal128](valVec, 1), "midpoint of 10 and 30") + require.True(t, valVec.IsNull(3), "partition 1's trailing NULL must stay NULL") + require.True(t, valVec.IsNull(4), "partition 2's leading NULL must stay NULL") + require.False(t, valVec.IsNull(5)) +} diff --git a/pkg/sql/colexec/fill/spill.go b/pkg/sql/colexec/fill/spill.go new file mode 100644 index 0000000000000..9f395a07932ad --- /dev/null +++ b/pkg/sql/colexec/fill/spill.go @@ -0,0 +1,757 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fill + +import ( + "bytes" + "fmt" + "io" + "os" + + "github.com/google/uuid" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "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/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/colexec" + "github.com/matrixorigin/matrixone/pkg/vm" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +const fillSpillMagic = uint64(0x46494c4c5350494c) + +type fillSpill struct { + input *os.File + output *os.File + next *os.File + buf bytes.Buffer + + outputReversePos int64 + ready bool + inputRecords int + outputRecords int + replay *batch.Batch + segmentPending []int64 + segmentLeftValid []bool + segmentStart int64 + segmentRows int64 + safeWatermark int64 + safeRows int64 + hasSuffix bool + linearLeft []*vector.Vector + linearLeftValid []bool + forwardPart spillPartitionSnapshot +} + +type spillPartitionSnapshot struct { + keys [][]byte + nulls []bool + set bool +} + +func (s *spillPartitionSnapshot) cloneFrom(src *spillPartitionSnapshot) { + *s = spillPartitionSnapshot{set: src.set} + if len(src.keys) > 0 { + s.keys = make([][]byte, len(src.keys)) + for i := range src.keys { + s.keys[i] = append([]byte(nil), src.keys[i]...) + } + } + s.nulls = append([]bool(nil), src.nulls...) +} + +func addOriginalNullMarkers(bat *batch.Batch, colLen int, mp *mpool.MPool) error { + rows := bat.RowCount() + for c := 0; c < colLen; c++ { + marker := vector.NewVec(types.T_bool.ToType()) + values := make([]bool, rows) + for r := 0; r < rows; r++ { + values[r] = bat.Vecs[c].IsNull(uint64(r)) + } + if err := vector.AppendFixedList(marker, values, nil, mp); err != nil { + marker.Free(mp) + return err + } + bat.Vecs = append(bat.Vecs, marker) + bat.Attrs = append(bat.Attrs, "") + } + return nil +} + +func stripOriginalNullMarkers(bat *batch.Batch, colLen int, mp *mpool.MPool) { + if bat == nil || colLen == 0 || len(bat.Vecs) < colLen { + return + } + start := len(bat.Vecs) - colLen + for _, vec := range bat.Vecs[start:] { + if vec != nil { + vec.Free(mp) + } + } + bat.Vecs = bat.Vecs[:start] + if len(bat.Attrs) >= colLen { + bat.Attrs = bat.Attrs[:len(bat.Attrs)-colLen] + } +} + +func originalNullAt(bat *batch.Batch, colLen, col, row int) bool { + marker := bat.Vecs[len(bat.Vecs)-colLen+col] + return vector.GetFixedAtNoTypeCheck[bool](marker, row) +} + +func newFillSpill(proc *process.Process) (*fillSpill, error) { + fs, err := proc.GetSpillFileService() + if err != nil { + return nil, err + } + input, err := fs.CreateAndRemoveFile(proc.Ctx, fmt.Sprintf("fill_%s_in", uuid.NewString())) + if err != nil { + return nil, err + } + return &fillSpill{input: input}, nil +} + +func (s *fillSpill) ensureOutput(proc *process.Process) error { + if s.output != nil { + return nil + } + fs, err := proc.GetSpillFileService() + if err != nil { + return err + } + s.output, err = fs.CreateAndRemoveFile(proc.Ctx, fmt.Sprintf("fill_%s_out", uuid.NewString())) + return err +} + +func (s *fillSpill) ensureNext(proc *process.Process) error { + if s.next != nil { + return nil + } + fs, err := proc.GetSpillFileService() + if err != nil { + return err + } + s.next, err = fs.CreateAndRemoveFile(proc.Ctx, fmt.Sprintf("fill_%s_next", uuid.NewString())) + return err +} + +func (s *fillSpill) writeRecord(fd *os.File, bat *batch.Batch) error { + s.buf.Reset() + var zero int64 + s.buf.Write(types.EncodeInt64(&zero)) + start := s.buf.Len() + if _, err := bat.MarshalBinaryWithBuffer(&s.buf, false); err != nil { + return err + } + size := int64(s.buf.Len() - start) + copy(s.buf.Bytes()[:8], types.EncodeInt64(&size)) + s.buf.Write(types.EncodeInt64(&size)) + magic := fillSpillMagic + s.buf.Write(types.EncodeUint64(&magic)) + n, err := fd.Write(s.buf.Bytes()) + if err != nil { + return err + } + if n != s.buf.Len() { + return io.ErrShortWrite + } + return nil +} + +func readRecordReverse(fd *os.File, pos *int64, mp *mpool.MPool, reuse *batch.Batch) (*batch.Batch, error) { + if *pos < 0 { + end, err := fd.Seek(0, io.SeekEnd) + if err != nil { + return nil, err + } + *pos = end + } + if *pos == 0 { + return nil, io.EOF + } + if *pos < 24 { + return nil, moerr.NewInternalErrorNoCtx("truncated fill spill record") + } + var tail [16]byte + if _, err := fd.ReadAt(tail[:], *pos-16); err != nil { + return nil, err + } + size := types.DecodeInt64(tail[:8]) + if types.DecodeUint64(tail[8:]) != fillSpillMagic || size < 0 { + return nil, moerr.NewInternalErrorNoCtx("corrupted fill spill record") + } + start := *pos - size - 24 + if start < 0 { + return nil, moerr.NewInternalErrorNoCtx("invalid fill spill record size") + } + var head [8]byte + if _, err := fd.ReadAt(head[:], start); err != nil { + return nil, err + } + if types.DecodeInt64(head[:]) != size { + return nil, moerr.NewInternalErrorNoCtx("fill spill record length mismatch") + } + section := io.NewSectionReader(fd, start+8, size) + allocated := reuse == nil + if reuse == nil { + reuse = batch.NewWithSize(0) + } else { + reuse.CleanOnlyData() + } + if err := reuse.UnmarshalFromReader(section, mp); err != nil { + if allocated { + reuse.Clean(mp) + } + return nil, err + } + consumed, err := section.Seek(0, io.SeekCurrent) + if err != nil { + if allocated { + reuse.Clean(mp) + } + return nil, err + } + if consumed != size { + if allocated { + reuse.Clean(mp) + } + return nil, moerr.NewInternalErrorNoCtx("fill spill record payload length mismatch") + } + *pos = start + return reuse, nil +} + +func (s *fillSpill) close(proc *process.Process) { + if s == nil { + return + } + if s.input != nil { + _ = s.input.Close() + s.input = nil + } + if s.output != nil { + _ = s.output.Close() + s.output = nil + } + if s.next != nil { + _ = s.next.Close() + s.next = nil + } + if s.replay != nil { + s.replay.Clean(proc.Mp()) + s.replay = nil + } + for _, vec := range s.linearLeft { + if vec != nil { + vec.Free(proc.Mp()) + } + } + s.linearLeft = nil + s.linearLeftValid = nil +} + +func (ctr *container) cleanupSpill(proc *process.Process) { + if ctr.spill != nil { + ctr.spill.close(proc) + ctr.spill = nil + } +} + +func (s *spillPartitionSnapshot) sameAndSet(partIdx []int32, bat *batch.Batch, row int) bool { + same := s.set + if cap(s.keys) < len(partIdx) { + s.keys = make([][]byte, len(partIdx)) + s.nulls = make([]bool, len(partIdx)) + } + s.keys = s.keys[:len(partIdx)] + s.nulls = s.nulls[:len(partIdx)] + for i, col := range partIdx { + value, isNull := partKeyAt(bat.Vecs[col], row) + if s.set && (isNull != s.nulls[i] || (!isNull && !bytes.Equal(value, s.keys[i]))) { + same = false + } + s.nulls[i] = isNull + s.keys[i] = append(s.keys[i][:0], value...) + } + s.set = true + return same +} + +func makeEndpoint(vec *vector.Vector, row int, proc *process.Process) (*vector.Vector, error) { + result := vector.NewVec(*vec.GetType()) + if err := appendValue(result, vec, row, proc); err != nil { + result.Free(proc.Mp()) + return nil, err + } + return result, nil +} + +func setEndpoint(dst **vector.Vector, src *vector.Vector, row int, proc *process.Process) error { + if *dst == nil { + var err error + *dst, err = makeEndpoint(src, row, proc) + return err + } + return setValue(*dst, src, 0, row, proc) +} + +func clearEndpoints(valid []bool) { + for i := range valid { + valid[i] = false + } +} + +func cloneBatchWindow(bat *batch.Batch, start, end int, mp *mpool.MPool) (*batch.Batch, error) { + result := batch.NewWithSize(len(bat.Vecs)) + result.Attrs = append(result.Attrs, bat.Attrs...) + for i, vec := range bat.Vecs { + cloned, err := vec.CloneWindow(start, end, mp) + if err != nil { + result.Clean(mp) + return nil, err + } + result.SetVector(int32(i), cloned) + } + result.SetRowCount(end - start) + return result, nil +} + +func (s *fillSpill) transformReverse(ap *Fill, proc *process.Process) error { + if err := s.ensureOutput(proc); err != nil { + return err + } + pos := int64(-1) + var reuse *batch.Batch + next := make([]*vector.Vector, ap.ColLen) + valid := make([]bool, ap.ColLen) + defer func() { + if reuse != nil { + reuse.Clean(proc.Mp()) + } + for _, vec := range next { + if vec != nil { + vec.Free(proc.Mp()) + } + } + }() + var part spillPartitionSnapshot + for { + bat, err := readRecordReverse(s.input, &pos, proc.Mp(), reuse) + if err == io.EOF { + break + } + if err != nil { + return err + } + reuse = bat + for row := bat.RowCount() - 1; row >= 0; row-- { + if len(ap.PartitionColIdx) > 0 && !part.sameAndSet(ap.PartitionColIdx, bat, row) { + clearEndpoints(valid) + } + for col := 0; col < ap.ColLen; col++ { + if originalNullAt(bat, ap.ColLen, col, row) { + if valid[col] { + if err = setValue(bat.Vecs[col], next[col], row, 0, proc); err != nil { + return err + } + } else { + bat.Vecs[col].GetNulls().Add(uint64(row)) + } + continue + } + if err = setEndpoint(&next[col], bat.Vecs[col], row, proc); err != nil { + return err + } + valid[col] = true + } + } + if err = s.writeRecord(s.output, bat); err != nil { + return err + } + s.outputRecords++ + } + if _, err := s.output.Seek(0, io.SeekEnd); err != nil { + return err + } + s.outputReversePos = -1 + if ap.FillType == plan.Node_LINEAR { + if len(s.linearLeft) < ap.ColLen { + s.linearLeft = make([]*vector.Vector, ap.ColLen) + } + if len(s.linearLeftValid) < ap.ColLen { + s.linearLeftValid = make([]bool, ap.ColLen) + } + } + s.ready = true + return nil +} + +func (ctr *container) shouldSpillPending() bool { + return colexec.ShouldSpill(ctr.pendingBytes, ctr.pendingRows, ctr.spillThreshold) +} + +func (s *fillSpill) updateSafeWatermark() { + watermark := s.segmentRows + for _, pending := range s.segmentPending { + if pending >= 0 && pending < watermark { + watermark = pending + } + } + if watermark > s.safeWatermark { + s.safeWatermark = watermark + } +} + +// scanSegment advances the earliest unresolved logical row for every fill +// column. The minimum of those positions is a safe output watermark: later +// rows may still be pending, but they cannot change anything before it. +func (s *fillSpill) scanSegment(ctr *container, ap *Fill, bat *batch.Batch) { + for row := 0; row < bat.RowCount(); row++ { + logicalRow := s.segmentRows + if ctr.isNewSegment(ap, bat, row) { + for col := range s.segmentPending { + s.segmentPending[col] = -1 + s.segmentLeftValid[col] = false + } + } + for col := 0; col < ap.ColLen; col++ { + isNull := originalNullAt(bat, ap.ColLen, col, row) + switch ap.FillType { + case plan.Node_NEXT: + if isNull { + if s.segmentPending[col] < 0 { + s.segmentPending[col] = logicalRow + } + } else { + s.segmentPending[col] = -1 + } + case plan.Node_LINEAR: + if isNull { + if s.segmentLeftValid[col] && s.segmentPending[col] < 0 { + s.segmentPending[col] = logicalRow + } + } else { + s.segmentPending[col] = -1 + s.segmentLeftValid[col] = true + } + } + } + s.segmentRows++ + s.updateSafeWatermark() + } +} + +func (s *fillSpill) finalizeSegment(ctr *container, ap *Fill, proc *process.Process) error { + s.safeRows = s.safeWatermark - s.segmentStart + if err := s.transformReverse(ap, proc); err != nil { + return err + } + if s.input != nil { + _ = s.input.Close() + s.input = nil + } + return nil +} + +func (ctr *container) beginSpill(ap *Fill, proc *process.Process) error { + spill, err := newFillSpill(proc) + if err != nil { + return err + } + spill.segmentPending = make([]int64, ap.ColLen) + spill.segmentLeftValid = make([]bool, ap.ColLen) + for _, bat := range ctr.bats { + spill.segmentRows += int64(bat.RowCount()) + } + coordOffset := func(coord fillCoord) int64 { + var offset int64 + for seq := ctr.baseSeq; seq < coord.seq; seq++ { + offset += int64(ctr.batAt(seq).RowCount()) + } + return offset + int64(coord.row) + } + for col := 0; col < ap.ColLen; col++ { + spill.segmentPending[col] = -1 + if ap.FillType == plan.Node_NEXT { + if len(ctr.nextRun[col]) > 0 { + coord := ctr.nextRun[col][0] + spill.segmentPending[col] = coordOffset(coord) + } + } else { + if len(ctr.linRun[col]) > 0 { + coord := ctr.linRun[col][0] + spill.segmentPending[col] = coordOffset(coord) + } + spill.segmentLeftValid[col] = ctr.linPre[col].seq >= 0 || ctr.linSeedValid[col] + } + } + spill.updateSafeWatermark() + if ap.FillType == plan.Node_LINEAR && len(ctr.linEntry) > 0 { + spill.linearLeft = ctr.linEntry + spill.linearLeftValid = ctr.linEntryValid + spill.forwardPart.cloneFrom(&ctr.linEntryPart) + ctr.linEntry = make([]*vector.Vector, ap.ColLen) + ctr.linEntryValid = make([]bool, ap.ColLen) + ctr.linEntryPart = spillPartitionSnapshot{} + } + for i, bat := range ctr.bats { + if err = spill.writeRecord(spill.input, bat); err != nil { + spill.close(proc) + return err + } + spill.inputRecords++ + bat.Clean(proc.Mp()) + ctr.bats[i] = nil + } + ctr.bats = ctr.bats[:0] + ctr.pendingBytes = 0 + ctr.pendingRows = 0 + ctr.flushable = 0 + ctr.baseSeq = 0 + if ap.FillType == plan.Node_NEXT { + ctr.flushPendingRunsNext(ap) + } else { + ctr.flushPendingRunsLinear(ap) + } + ctr.spill = spill + if spill.safeWatermark > spill.segmentStart { + if err = spill.finalizeSegment(ctr, ap, proc); err != nil { + ctr.cleanupSpill(proc) + return err + } + } + return nil +} + +func (ctr *container) collectSpill(ap *Fill, proc *process.Process, analyzer process.Analyzer) error { + for { + result, err := vm.ChildrenCall(ap.GetChildren(0), proc, analyzer) + if err != nil { + return err + } + if result.Batch == nil { + ctr.childDone = true + ctr.spill.safeWatermark = ctr.spill.segmentRows + return ctr.spill.finalizeSegment(ctr, ap, proc) + } + dup, err := result.Batch.Dup(proc.Mp()) + if err != nil { + return err + } + if err = addOriginalNullMarkers(dup, ap.ColLen, proc.Mp()); err != nil { + dup.Clean(proc.Mp()) + return err + } + ctr.spill.scanSegment(ctr, ap, dup) + if err = ctr.spill.writeRecord(ctr.spill.input, dup); err != nil { + dup.Clean(proc.Mp()) + return err + } + ctr.spill.inputRecords++ + if dup.RowCount() > 0 && len(ap.PartitionColIdx) > 0 { + ctr.snapshotPartKey(ap.PartitionColIdx, dup, dup.RowCount()-1) + } + if analyzer != nil { + analyzer.Spill(int64(ctr.spill.buf.Len())) + analyzer.SpillRows(int64(dup.RowCount())) + } + dup.Clean(proc.Mp()) + if ctr.spill.safeWatermark > ctr.spill.segmentStart { + return ctr.spill.finalizeSegment(ctr, ap, proc) + } + } +} + +func (s *fillSpill) replayNext(ctr *container, ap *Fill, proc *process.Process) (*batch.Batch, error) { + if s.replay != nil { + // The batch returned by the previous Call had its internal marker columns + // stripped. Reusing it for a wider spilled record would make batch + // UnmarshalFromReader clean the batch after setting RowCount, losing the + // decoded count. Release it and keep the replay footprint at one fresh + // batch instead. + s.replay.Clean(proc.Mp()) + s.replay = nil + } + for { + bat, err := readRecordReverse(s.output, &s.outputReversePos, proc.Mp(), nil) + if err != nil { + return nil, err + } + rows := int64(bat.RowCount()) + if s.safeRows <= 0 { + if err = s.writeSuffix(proc, bat); err != nil { + bat.Clean(proc.Mp()) + return nil, err + } + bat.Clean(proc.Mp()) + continue + } + if s.safeRows >= rows { + s.safeRows -= rows + s.replay = bat + if ap.FillType == plan.Node_LINEAR { + if err = s.finishLinearBatch(ctr, ap, proc, bat); err != nil { + return nil, err + } + } + stripOriginalNullMarkers(bat, ap.ColLen, proc.Mp()) + return bat, nil + } + + end := int(s.safeRows) + prefix, err := cloneBatchWindow(bat, 0, end, proc.Mp()) + if err != nil { + bat.Clean(proc.Mp()) + return nil, err + } + suffix, err := cloneBatchWindow(bat, end, bat.RowCount(), proc.Mp()) + if err != nil { + prefix.Clean(proc.Mp()) + bat.Clean(proc.Mp()) + return nil, err + } + bat.Clean(proc.Mp()) + if err = s.writeSuffix(proc, suffix); err != nil { + prefix.Clean(proc.Mp()) + suffix.Clean(proc.Mp()) + return nil, err + } + suffix.Clean(proc.Mp()) + s.safeRows = 0 + s.replay = prefix + if ap.FillType == plan.Node_LINEAR { + if err = s.finishLinearBatch(ctr, ap, proc, prefix); err != nil { + return nil, err + } + } + stripOriginalNullMarkers(prefix, ap.ColLen, proc.Mp()) + return prefix, nil + } +} + +func (s *fillSpill) writeSuffix(proc *process.Process, bat *batch.Batch) error { + if err := s.ensureNext(proc); err != nil { + return err + } + if err := s.writeRecord(s.next, bat); err != nil { + return err + } + s.hasSuffix = true + return nil +} + +func (s *fillSpill) rotateSuffix() { + if s.output != nil { + _ = s.output.Close() + s.output = nil + } + s.input = s.next + s.next = nil + s.outputReversePos = -1 + s.ready = false + s.hasSuffix = false + s.segmentStart = s.safeWatermark + s.safeRows = 0 + s.inputRecords = 0 + s.outputRecords = 0 +} + +func (ctr *container) finishSpillReplay( + ap *Fill, + proc *process.Process, +) error { + spill := ctr.spill + if spill.hasSuffix { + spill.rotateSuffix() + return nil + } + if ap.FillType == plan.Node_LINEAR { + seed := make([]*vector.Vector, ap.ColLen) + seedValid := make([]bool, ap.ColLen) + for col := 0; col < ap.ColLen; col++ { + if !spill.linearLeftValid[col] { + continue + } + var err error + seed[col], err = makeEndpoint(spill.linearLeft[col], 0, proc) + if err != nil { + for _, vec := range seed { + if vec != nil { + vec.Free(proc.Mp()) + } + } + return err + } + seedValid[col] = true + } + ctr.clearLinearSeeds(proc.Mp()) + ctr.clearLinearEntries(proc.Mp()) + ctr.linSeed = seed + ctr.linSeedValid = seedValid + ctr.linEntry = spill.linearLeft + ctr.linEntryValid = spill.linearLeftValid + ctr.linEntryPart.cloneFrom(&spill.forwardPart) + spill.linearLeft = nil + spill.linearLeftValid = nil + } + ctr.cleanupSpill(proc) + return nil +} + +func (s *fillSpill) finishLinearBatch(ctr *container, ap *Fill, proc *process.Process, bat *batch.Batch) error { + for row := 0; row < bat.RowCount(); row++ { + if len(ap.PartitionColIdx) > 0 { + wasSet := s.forwardPart.set + if !s.forwardPart.sameAndSet(ap.PartitionColIdx, bat, row) && wasSet { + clearEndpoints(s.linearLeftValid) + } + } + for col := 0; col < ap.ColLen; col++ { + if !originalNullAt(bat, ap.ColLen, col, row) { + if err := setEndpoint(&s.linearLeft[col], bat.Vecs[col], row, proc); err != nil { + return err + } + s.linearLeftValid[col] = true + continue + } + if bat.Vecs[col].IsNull(uint64(row)) || !s.linearLeftValid[col] { + bat.Vecs[col].GetNulls().Add(uint64(row)) + continue + } + leftBatch := batch.NewWithSize(col + 1) + leftBatch.SetVector(int32(col), s.linearLeft[col]) + leftBatch.SetRowCount(1) + rightBatch := batch.NewWithSize(col + 1) + rightBatch.SetVector(int32(col), bat.Vecs[col]) + rightBatch.SetRowCount(bat.RowCount()) + value, owned, err := linearFillValue(ctr, proc, col, leftBatch, 0, rightBatch, row) + if err != nil { + return err + } + if err = setValue(bat.Vecs[col], value, row, 0, proc); err != nil { + if owned { + value.Free(proc.Mp()) + } + return err + } + if owned { + value.Free(proc.Mp()) + } + } + } + return nil +} diff --git a/pkg/sql/colexec/fill/streaming_test.go b/pkg/sql/colexec/fill/streaming_test.go new file mode 100644 index 0000000000000..e9e640976c658 --- /dev/null +++ b/pkg/sql/colexec/fill/streaming_test.go @@ -0,0 +1,947 @@ +// Copyright 2021 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fill + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "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/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/colexec" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/vm" + "github.com/matrixorigin/matrixone/pkg/vm/process" + "github.com/stretchr/testify/require" +) + +// countingChild records how many times the operator was pulled, so a test can +// assert that fill emitted a result before draining the whole child. +type countingChild struct { + *colexec.MockOperator + calls int +} + +var _ vm.Operator = (*countingChild)(nil) + +func (c *countingChild) Call(proc *process.Process) (vm.CallResult, error) { + c.calls++ + return c.MockOperator.Call(proc) +} + +type cell struct { + val int64 + null bool +} + +func readCol(bat *batch.Batch, col int) []cell { + vec := bat.Vecs[col] + out := make([]cell, 0, bat.RowCount()) + for r := 0; r < bat.RowCount(); r++ { + if vec.IsNull(uint64(r)) { + out = append(out, cell{null: true}) + } else { + out = append(out, cell{val: vector.GetFixedAtNoTypeCheck[int64](vec, r)}) + } + } + return out +} + +// drainCol keeps calling the operator and concatenates column col of every +// emitted batch. Each batch stays valid until the next Call frees it, so it is +// read before advancing. +func drainCol(t *testing.T, arg *Fill, proc *process.Process, col int) []cell { + t.Helper() + var out []cell + for { + res, err := arg.Call(proc) + require.NoError(t, err) + if res.Batch == nil { + return out + } + out = append(out, readCol(res.Batch, col)...) + } +} + +// A no-NULL NEXT stream must flow straight through: the first batch is emitted +// after a single child pull, never after materializing the whole input. This +// is the regression the reviewer asked for — an outer LIMIT 1 could stop the +// child right here. +func TestFillNextStreamsWithoutBuffering(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + const n = 5 + bats := make([]*batch.Batch, 0, n) + for i := 0; i < n; i++ { + bats = append(bats, partitionedBatch(proc.Mp(), + []int64{int64(2 * i), int64(2*i + 1)}, nil, []int64{1, 1})) + } + + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 1, FillType: plan.Node_NEXT, PartitionColIdx: []int32{1}} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + res, err := arg.Call(proc) + require.NoError(t, err) + require.NotNil(t, res.Batch) + require.Equal(t, 1, child.calls, "first batch must be emitted after exactly one child pull") + require.LessOrEqual(t, len(arg.ctr.bats), 1, "no-NULL NEXT must not buffer the stream") + + all := append(readCol(res.Batch, 0), drainCol(t, arg, proc, 0)...) + require.Len(t, all, 2*n) + for i, c := range all { + require.False(t, c.null) + require.Equal(t, int64(i), c.val) + } + require.Equal(t, n+1, child.calls, "the child is drained exactly once end to end") + + arg.Free(proc, false, nil) + for _, b := range bats { + b.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// LINEAR keeps at most one batch buffered while streaming a no-NULL input: it +// pins the last non-NULL as a possible left endpoint, so it emits batch k only +// after batch k+1 arrives — bounded, not the whole input. +func TestFillLinearStreamsBounded(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + const n = 5 + bats := make([]*batch.Batch, 0, n) + for i := 0; i < n; i++ { + bats = append(bats, partitionedBatch(proc.Mp(), + []int64{int64(2 * i), int64(2*i + 1)}, nil, []int64{1, 1})) + } + + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 1, FillType: plan.Node_LINEAR, PartitionColIdx: []int32{1}} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + res, err := arg.Call(proc) + require.NoError(t, err) + require.NotNil(t, res.Batch) + require.Equal(t, 2, child.calls, "LINEAR emits its first batch after a one-batch look-ahead") + require.LessOrEqual(t, len(arg.ctr.bats), 1, "LINEAR buffers at most one batch on a no-NULL stream") + + all := append(readCol(res.Batch, 0), drainCol(t, arg, proc, 0)...) + require.Len(t, all, 2*n) + for i, c := range all { + require.False(t, c.null) + require.Equal(t, int64(i), c.val) + } + + arg.Free(proc, false, nil) + for _, b := range bats { + b.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// A NULL that spans several batches is filled by the value that finally arrives +// downstream, no matter how far away it is. +func TestFillNextLongCrossBatchGap(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{40}, nil, []int64{1}), + } + + vals, nulls := runFill(t, proc, plan.Node_NEXT, bats) + require.Equal(t, []bool{false, false, false, false}, nulls) + require.Equal(t, []int64{10, 40, 40, 40}, vals) + + for _, b := range bats { + b.Clean(proc.Mp()) + } + proc.Free() +} + +func TestFillNextLongGapSpillsPendingSuffix(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + const nullBatches = 64 + bats := make([]*batch.Batch, 0, nullBatches+2) + bats = append(bats, partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1})) + for i := 0; i < nullBatches; i++ { + bats = append(bats, partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1})) + } + bats = append(bats, partitionedBatch(proc.Mp(), []int64{90}, nil, []int64{1})) + + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 1, FillType: plan.Node_NEXT, PartitionColIdx: []int32{1}, SpillThreshold: 2} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{val: 10}}, readCol(first.Batch, 0)) + require.Equal(t, 1, child.calls, "resolved prefix must still stream before spill") + + second, err := arg.Call(proc) + require.NoError(t, err) + require.NotNil(t, second.Batch) + require.NotNil(t, arg.ctr.spill) + require.Equal(t, arg.ctr.spill.inputRecords, arg.ctr.spill.outputRecords) + require.Empty(t, arg.ctr.bats) + require.Zero(t, arg.ctr.pendingBytes) + require.Zero(t, arg.ctr.pendingRows) + + all := append(readCol(second.Batch, 0), drainCol(t, arg, proc, 0)...) + require.Len(t, all, nullBatches+1) + for _, value := range all { + require.Equal(t, cell{val: 90}, value) + } + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillNextSpillReplaysClosedSegmentBeforeChildEOF(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{40, 50, 60}, nil, []int64{1, 1, 1}), + } + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 1, FillType: plan.Node_NEXT, PartitionColIdx: []int32{1}, SpillThreshold: 2} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{val: 10}}, readCol(first.Batch, 0)) + require.Equal(t, 1, child.calls) + + second, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{val: 40}}, readCol(second.Batch, 0)) + require.Equal(t, 4, child.calls, "closed segment must replay before the carry tail or EOF is pulled") + + all := append(readCol(second.Batch, 0), drainCol(t, arg, proc, 0)...) + require.Equal(t, []cell{{val: 40}, {val: 40}, {val: 40}, {val: 50}, {val: 60}}, all) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillNextSpillPartitionBoundaryClosesSegment(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{20, 30}, nil, []int64{2, 2}), + } + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 1, FillType: plan.Node_NEXT, PartitionColIdx: []int32{1}, SpillThreshold: 1} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{val: 10}}, readCol(first.Batch, 0)) + + second, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{null: true}}, readCol(second.Batch, 0)) + require.Equal(t, 3, child.calls, "partition boundary must close the old segment before its carry rows") + + all := append(readCol(second.Batch, 0), drainCol(t, arg, proc, 0)...) + require.Equal(t, []cell{{null: true}, {val: 20}, {val: 30}}, all) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// Trailing NULLs with no following value in the stream stay NULL at EOF rather +// than being dropped or hanging the operator. +func TestFillNextEOFTail(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0, 0}, []uint64{0, 1}, []int64{1, 1}), + } + + vals, nulls := runFill(t, proc, plan.Node_NEXT, bats) + require.Equal(t, []bool{false, true, true}, nulls) + require.Equal(t, int64(10), vals[0]) + + for _, b := range bats { + b.Clean(proc.Mp()) + } + proc.Free() +} + +func TestFillNextSpillKeepsEOFTailNull(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + } + child := colexec.NewMockOperator().WithBatchs(bats) + arg := &Fill{ColLen: 1, FillType: plan.Node_NEXT, PartitionColIdx: []int32{1}, SpillThreshold: 2} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + got := drainCol(t, arg, proc, 0) + require.Equal(t, []cell{{val: 10}, {null: true}, {null: true}}, got) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// Two fill columns resolve independently: a row leaves the operator only once +// every filled column has a value, and each column's gap is closed by its own +// next value. +func TestFillNextMultiColumnGap(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + // col0: [1, NULL, 3]; col1: [NULL, 20, 30]; part all 1. + bat := batch.NewWithSize(3) + bat.SetVector(0, testutil.MakeInt64Vector([]int64{1, 0, 3}, []uint64{1}, proc.Mp())) + bat.SetVector(1, testutil.MakeInt64Vector([]int64{0, 20, 30}, []uint64{0}, proc.Mp())) + bat.SetVector(2, testutil.MakeInt64Vector([]int64{1, 1, 1}, nil, proc.Mp())) + bat.SetRowCount(3) + + child := colexec.NewMockOperator().WithBatchs([]*batch.Batch{bat}) + arg := &Fill{ColLen: 2, FillType: plan.Node_NEXT, PartitionColIdx: []int32{2}} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + var col0, col1 []cell + for { + res, err := arg.Call(proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + col0 = append(col0, readCol(res.Batch, 0)...) + col1 = append(col1, readCol(res.Batch, 1)...) + } + require.Equal(t, []cell{{val: 1}, {val: 3}, {val: 3}}, col0, "col0 gap filled by its own next value") + require.Equal(t, []cell{{val: 20}, {val: 20}, {val: 30}}, col1, "col1 gap filled independently") + + arg.Free(proc, false, nil) + bat.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillNextSpillMultiColumnAndPartitionBoundary(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + makeBatch := func(col0, col1 []int64, null0, null1 []uint64, parts []int64) *batch.Batch { + bat := batch.NewWithSize(3) + bat.SetVector(0, testutil.MakeInt64Vector(col0, null0, proc.Mp())) + bat.SetVector(1, testutil.MakeInt64Vector(col1, null1, proc.Mp())) + bat.SetVector(2, testutil.MakeInt64Vector(parts, nil, proc.Mp())) + bat.SetRowCount(len(parts)) + return bat + } + bats := []*batch.Batch{ + makeBatch([]int64{1}, []int64{0}, nil, []uint64{0}, []int64{1}), + makeBatch([]int64{0}, []int64{20}, []uint64{0}, nil, []int64{1}), + makeBatch([]int64{3}, []int64{30}, nil, nil, []int64{1}), + // Partition 1's candidates must not fill partition 2's leading NULLs. + makeBatch([]int64{0}, []int64{0}, []uint64{0}, []uint64{0}, []int64{2}), + makeBatch([]int64{8}, []int64{80}, nil, nil, []int64{2}), + } + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 2, FillType: plan.Node_NEXT, PartitionColIdx: []int32{2}, SpillThreshold: 1} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, 2, child.calls, "safe watermark must advance before both columns close simultaneously") + col0 := readCol(first.Batch, 0) + col1 := readCol(first.Batch, 1) + for { + res, err := arg.Call(proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + col0 = append(col0, readCol(res.Batch, 0)...) + col1 = append(col1, readCol(res.Batch, 1)...) + } + require.Equal(t, []cell{{val: 1}, {val: 3}, {val: 3}, {val: 8}, {val: 8}}, col0) + require.Equal(t, []cell{{val: 20}, {val: 20}, {val: 30}, {val: 80}, {val: 80}}, col1) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillNextSpillAlternatingColumnsAdvancesWatermark(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + makeBatch := func(col0, col1 int64, null0, null1 bool) *batch.Batch { + bat := batch.NewWithSize(3) + var nulls0, nulls1 []uint64 + if null0 { + nulls0 = []uint64{0} + } + if null1 { + nulls1 = []uint64{0} + } + bat.SetVector(0, testutil.MakeInt64Vector([]int64{col0}, nulls0, proc.Mp())) + bat.SetVector(1, testutil.MakeInt64Vector([]int64{col1}, nulls1, proc.Mp())) + bat.SetVector(2, testutil.MakeInt64Vector([]int64{1}, nil, proc.Mp())) + bat.SetRowCount(1) + return bat + } + bats := []*batch.Batch{ + makeBatch(1, 0, false, true), + makeBatch(0, 20, true, false), + makeBatch(3, 0, false, true), + makeBatch(0, 40, true, false), + makeBatch(5, 50, false, false), + } + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 2, FillType: plan.Node_NEXT, PartitionColIdx: []int32{2}, SpillThreshold: 1} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, 2, child.calls, "alternating pending columns must not force an EOF drain") + col0 := readCol(first.Batch, 0) + col1 := readCol(first.Batch, 1) + for { + result, callErr := arg.Call(proc) + require.NoError(t, callErr) + if result.Batch == nil { + break + } + col0 = append(col0, readCol(result.Batch, 0)...) + col1 = append(col1, readCol(result.Batch, 1)...) + } + require.Equal(t, []cell{{val: 1}, {val: 3}, {val: 3}, {val: 5}, {val: 5}}, col0) + require.Equal(t, []cell{{val: 20}, {val: 20}, {val: 40}, {val: 40}, {val: 50}}, col1) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillLinearSpillAlternatingColumnsAdvancesWatermark(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + makeBatch := func(col0, col1 int64, null0, null1 bool) *batch.Batch { + bat := batch.NewWithSize(3) + var nulls0, nulls1 []uint64 + if null0 { + nulls0 = []uint64{0} + } + if null1 { + nulls1 = []uint64{0} + } + bat.SetVector(0, testutil.MakeInt64Vector([]int64{col0}, nulls0, proc.Mp())) + bat.SetVector(1, testutil.MakeInt64Vector([]int64{col1}, nulls1, proc.Mp())) + bat.SetVector(2, testutil.MakeInt64Vector([]int64{1}, nil, proc.Mp())) + bat.SetRowCount(1) + return bat + } + bats := []*batch.Batch{ + makeBatch(10, 0, false, true), + makeBatch(0, 20, true, false), + makeBatch(30, 0, false, true), + makeBatch(0, 40, true, false), + makeBatch(50, 60, false, false), + } + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 2, FillType: plan.Node_LINEAR, PartitionColIdx: []int32{2}, SpillThreshold: 1} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + midpoint := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vector.AppendFixed(midpoint, int64(99), false, proc.Mp())) + stub := &fillStubExpressionExecutor{result: midpoint} + arg.ctr.exes = []colexec.ExpressionExecutor{stub, stub} + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, 1, child.calls) + col0 := readCol(first.Batch, 0) + col1 := readCol(first.Batch, 1) + second, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, 3, child.calls, "alternating LINEAR gaps must advance before child EOF") + col0 = append(col0, readCol(second.Batch, 0)...) + col1 = append(col1, readCol(second.Batch, 1)...) + for { + result, callErr := arg.Call(proc) + require.NoError(t, callErr) + if result.Batch == nil { + break + } + col0 = append(col0, readCol(result.Batch, 0)...) + col1 = append(col1, readCol(result.Batch, 1)...) + } + require.Equal(t, []cell{{val: 10}, {val: 99}, {val: 30}, {val: 99}, {val: 50}}, col0) + require.Equal(t, []cell{{null: true}, {val: 20}, {val: 99}, {val: 40}, {val: 60}}, col1) + + arg.Free(proc, false, nil) + midpoint.Free(proc.Mp()) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillSpillWatermarkSplitsBatch(t *testing.T) { + for _, fillType := range []plan.Node_FillType{plan.Node_NEXT, plan.Node_LINEAR} { + t.Run(fillType.String(), func(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + firstInput := batch.NewWithSize(3) + firstInput.SetVector(0, testutil.MakeInt64Vector([]int64{10, 0}, []uint64{1}, proc.Mp())) + firstInput.SetVector(1, testutil.MakeInt64Vector([]int64{0, 20}, []uint64{0}, proc.Mp())) + firstInput.SetVector(2, testutil.MakeInt64Vector([]int64{1, 1}, nil, proc.Mp())) + firstInput.SetRowCount(2) + secondInput := batch.NewWithSize(3) + secondInput.SetVector(0, testutil.MakeInt64Vector([]int64{30}, nil, proc.Mp())) + secondInput.SetVector(1, testutil.MakeInt64Vector([]int64{40}, nil, proc.Mp())) + secondInput.SetVector(2, testutil.MakeInt64Vector([]int64{1}, nil, proc.Mp())) + secondInput.SetRowCount(1) + bats := []*batch.Batch{firstInput, secondInput} + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 2, FillType: fillType, PartitionColIdx: []int32{2}, SpillThreshold: 2} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + midpoint := vector.NewVec(types.T_int64.ToType()) + if fillType == plan.Node_LINEAR { + require.NoError(t, vector.AppendFixed(midpoint, int64(99), false, proc.Mp())) + stub := &fillStubExpressionExecutor{result: midpoint} + arg.ctr.exes = []colexec.ExpressionExecutor{stub, stub} + } + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, 1, child.calls, "batch-local watermark must replay before the next child batch") + col0 := readCol(first.Batch, 0) + col1 := readCol(first.Batch, 1) + for { + result, callErr := arg.Call(proc) + require.NoError(t, callErr) + if result.Batch == nil { + break + } + col0 = append(col0, readCol(result.Batch, 0)...) + col1 = append(col1, readCol(result.Batch, 1)...) + } + if fillType == plan.Node_NEXT { + require.Equal(t, []cell{{val: 10}, {val: 30}, {val: 30}}, col0) + require.Equal(t, []cell{{val: 20}, {val: 20}, {val: 40}}, col1) + } else { + require.Equal(t, []cell{{val: 10}, {val: 99}, {val: 30}}, col0) + require.Equal(t, []cell{{null: true}, {val: 20}, {val: 40}}, col1) + } + + arg.Free(proc, false, nil) + if fillType == plan.Node_LINEAR { + midpoint.Free(proc.Mp()) + } + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) + }) + } +} + +// LINEAR interpolates across a batch boundary: the midpoint uses the non-NULL +// values that bracket the gap even when they land in different batches. +func TestFillLinearCrossBatchGap(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + typ := types.New(types.T_decimal128, 38, 0) + set := func(vec *vector.Vector, v int64, isNull bool) { + require.NoError(t, vector.AppendFixed(vec, types.Decimal128FromInt64(v), isNull, proc.Mp())) + } + // batch 1: [10, NULL]; batch 2: [NULL, 40]; one partition, so the midpoint + // of 10 and 40 (=25) fills both gaps. + v1 := vector.NewVec(typ) + set(v1, 10, false) + set(v1, 0, true) + b1 := batch.NewWithSize(2) + b1.SetVector(0, v1) + b1.SetVector(1, testutil.MakeInt64Vector([]int64{1, 1}, nil, proc.Mp())) + b1.SetRowCount(2) + + v2 := vector.NewVec(typ) + set(v2, 0, true) + set(v2, 40, false) + b2 := batch.NewWithSize(2) + b2.SetVector(0, v2) + b2.SetVector(1, testutil.MakeInt64Vector([]int64{1, 1}, nil, proc.Mp())) + b2.SetRowCount(2) + + bats := []*batch.Batch{b1, b2} + child := colexec.NewMockOperator().WithBatchs(bats) + arg := &Fill{ColLen: 1, FillType: plan.Node_LINEAR, PartitionColIdx: []int32{1}} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + var got []types.Decimal128 + var isNull []bool + for { + res, err := arg.Call(proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + vec := res.Batch.Vecs[0] + for r := 0; r < res.Batch.RowCount(); r++ { + isNull = append(isNull, vec.IsNull(uint64(r))) + got = append(got, vector.GetFixedAtNoTypeCheck[types.Decimal128](vec, r)) + } + } + require.Equal(t, []bool{false, false, false, false}, isNull) + require.Equal(t, types.Decimal128FromInt64(25), got[1], "midpoint of 10 and 40") + require.Equal(t, types.Decimal128FromInt64(25), got[2]) + + arg.Free(proc, false, nil) + for _, b := range bats { + b.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillLinearLongGapSpillsPendingSuffix(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + const nullBatches = 64 + typ := types.New(types.T_decimal128, 38, 0) + makeBatch := func(value int64, isNull bool) *batch.Batch { + vec := vector.NewVec(typ) + require.NoError(t, vector.AppendFixed(vec, types.Decimal128FromInt64(value), isNull, proc.Mp())) + bat := batch.NewWithSize(2) + bat.SetVector(0, vec) + bat.SetVector(1, testutil.MakeInt64Vector([]int64{1}, nil, proc.Mp())) + bat.SetRowCount(1) + return bat + } + bats := make([]*batch.Batch, 0, nullBatches+2) + bats = append(bats, makeBatch(10, false)) + for i := 0; i < nullBatches; i++ { + bats = append(bats, makeBatch(0, true)) + } + bats = append(bats, makeBatch(90, false)) + + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ + ColLen: 1, + FillType: plan.Node_LINEAR, + PartitionColIdx: []int32{1}, + SpillThreshold: 2, + } + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + first, err := arg.Call(proc) + require.NoError(t, err) + require.NotNil(t, first.Batch) + require.NotNil(t, arg.ctr.spill) + require.Empty(t, arg.ctr.bats) + require.Zero(t, arg.ctr.pendingBytes) + require.Zero(t, arg.ctr.pendingRows) + + readDecimal := func(bat *batch.Batch) []types.Decimal128 { + return append([]types.Decimal128(nil), vector.MustFixedColNoTypeCheck[types.Decimal128](bat.Vecs[0])...) + } + all := readDecimal(first.Batch) + for { + res, callErr := arg.Call(proc) + require.NoError(t, callErr) + if res.Batch == nil { + break + } + all = append(all, readDecimal(res.Batch)...) + } + require.Len(t, all, nullBatches+2) + require.Equal(t, types.Decimal128FromInt64(10), all[0]) + for _, value := range all[1 : len(all)-1] { + require.Equal(t, types.Decimal128FromInt64(50), value) + } + require.Equal(t, types.Decimal128FromInt64(90), all[len(all)-1]) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillLinearConsecutiveSpillsPreserveSegmentEntry(t *testing.T) { + t.Run("single decimal column", func(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + typ := types.New(types.T_decimal128, 38, 0) + makeBatch := func(values []int64, nulls []uint64, parts []int64) *batch.Batch { + vec := vector.NewVec(typ) + for row, value := range values { + require.NoError(t, vector.AppendFixed( + vec, types.Decimal128FromInt64(value), vec.GetNulls().Contains(uint64(row)), proc.Mp())) + } + for _, row := range nulls { + vec.GetNulls().Add(row) + } + bat := batch.NewWithSize(2) + bat.SetVector(0, vec) + bat.SetVector(1, testutil.MakeInt64Vector(parts, nil, proc.Mp())) + bat.SetRowCount(len(values)) + return bat + } + bats := []*batch.Batch{ + makeBatch([]int64{10}, nil, []int64{1}), + makeBatch([]int64{0, 20}, []uint64{0}, []int64{1, 1}), + } + arg := &Fill{ColLen: 1, FillType: plan.Node_LINEAR, PartitionColIdx: []int32{1}, SpillThreshold: 1} + arg.AppendChild(colexec.NewMockOperator().WithBatchs(bats)) + require.NoError(t, arg.Prepare(proc)) + + var got []types.Decimal128 + var nulls []bool + for { + res, err := arg.Call(proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + for row := 0; row < res.Batch.RowCount(); row++ { + nulls = append(nulls, res.Batch.Vecs[0].IsNull(uint64(row))) + got = append(got, vector.GetFixedAtNoTypeCheck[types.Decimal128](res.Batch.Vecs[0], row)) + } + } + require.Equal(t, []bool{false, false, false}, nulls) + require.Equal(t, types.Decimal128FromInt64(10), got[0]) + require.Equal(t, types.Decimal128FromInt64(15), got[1]) + require.Equal(t, types.Decimal128FromInt64(20), got[2]) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) + }) + + t.Run("multiple columns and partition boundary", func(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + makeBatch := func(col0, col1 []int64, nulls0, nulls1 []uint64, parts []int64) *batch.Batch { + bat := batch.NewWithSize(3) + bat.SetVector(0, testutil.MakeInt64Vector(col0, nulls0, proc.Mp())) + bat.SetVector(1, testutil.MakeInt64Vector(col1, nulls1, proc.Mp())) + bat.SetVector(2, testutil.MakeInt64Vector(parts, nil, proc.Mp())) + bat.SetRowCount(len(parts)) + return bat + } + bats := []*batch.Batch{ + makeBatch([]int64{10}, []int64{100}, nil, nil, []int64{1}), + makeBatch([]int64{0, 20}, []int64{0, 200}, []uint64{0}, []uint64{0}, []int64{1, 1}), + makeBatch([]int64{0, 40}, []int64{0, 400}, []uint64{0}, []uint64{0}, []int64{2, 2}), + } + arg := &Fill{ColLen: 2, FillType: plan.Node_LINEAR, PartitionColIdx: []int32{2}, SpillThreshold: 1} + arg.AppendChild(colexec.NewMockOperator().WithBatchs(bats)) + require.NoError(t, arg.Prepare(proc)) + midpoint := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vector.AppendFixed(midpoint, int64(99), false, proc.Mp())) + stub := &fillStubExpressionExecutor{result: midpoint} + arg.ctr.exes = []colexec.ExpressionExecutor{stub, stub} + + var col0, col1 []cell + for { + res, err := arg.Call(proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + col0 = append(col0, readCol(res.Batch, 0)...) + col1 = append(col1, readCol(res.Batch, 1)...) + } + require.Equal(t, []cell{{val: 10}, {val: 99}, {val: 20}, {null: true}, {val: 40}}, col0) + require.Equal(t, []cell{{val: 100}, {val: 99}, {val: 200}, {null: true}, {val: 400}}, col1) + + arg.Free(proc, false, nil) + midpoint.Free(proc.Mp()) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) + }) +} + +func TestFillLinearSpillReplaysClosedSegmentBeforeChildEOF(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{40, 50, 60}, nil, []int64{1, 1, 1}), + } + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ + ColLen: 1, + FillType: plan.Node_LINEAR, + PartitionColIdx: []int32{1}, + SpillThreshold: 2, + } + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + midpoint := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vector.AppendFixed(midpoint, int64(25), false, proc.Mp())) + arg.ctr.exes = []colexec.ExpressionExecutor{&fillStubExpressionExecutor{result: midpoint}} + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{val: 10}}, readCol(first.Batch, 0)) + require.Equal(t, 2, child.calls, "safe left endpoint must replay before the right endpoint or EOF") + + all := append(readCol(first.Batch, 0), drainCol(t, arg, proc, 0)...) + require.Equal(t, []cell{{val: 10}, {val: 25}, {val: 25}, {val: 40}, {val: 50}, {val: 60}}, all) + + arg.Free(proc, false, nil) + midpoint.Free(proc.Mp()) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillLinearSpillSeedStopsAtPartitionBoundary(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{10}, nil, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0, 20}, []uint64{0}, []int64{2, 2}), + } + child := &countingChild{MockOperator: colexec.NewMockOperator().WithBatchs(bats)} + arg := &Fill{ColLen: 1, FillType: plan.Node_LINEAR, PartitionColIdx: []int32{1}, SpillThreshold: 1} + arg.AppendChild(child) + require.NoError(t, arg.Prepare(proc)) + + first, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{val: 10}}, readCol(first.Batch, 0)) + + second, err := arg.Call(proc) + require.NoError(t, err) + require.Equal(t, []cell{{null: true}}, readCol(second.Batch, 0)) + require.Equal(t, 3, child.calls) + + all := append(readCol(second.Batch, 0), drainCol(t, arg, proc, 0)...) + require.Equal(t, []cell{{null: true}, {null: true}, {val: 20}}, all) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillSpillResetReleasesState(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + bats := []*batch.Batch{ + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{0}, []uint64{0}, []int64{1}), + partitionedBatch(proc.Mp(), []int64{90, 100}, nil, []int64{1, 1}), + } + arg := &Fill{ColLen: 1, FillType: plan.Node_NEXT, PartitionColIdx: []int32{1}, SpillThreshold: 2} + arg.AppendChild(colexec.NewMockOperator().WithBatchs(bats)) + require.NoError(t, arg.Prepare(proc)) + + result, err := arg.Call(proc) + require.NoError(t, err) + require.NotNil(t, result.Batch) + require.NotNil(t, arg.ctr.spill) + + arg.Reset(proc, false, nil) + require.Nil(t, arg.ctr.spill) + require.Empty(t, arg.ctr.bats) + require.Zero(t, arg.ctr.pendingBytes) + require.Zero(t, arg.ctr.pendingRows) + + arg.Free(proc, false, nil) + for _, bat := range bats { + bat.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestFillSpillResetReleasesWatermarkSuffix(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + input := batch.NewWithSize(3) + input.SetVector(0, testutil.MakeInt64Vector([]int64{10, 0}, []uint64{1}, proc.Mp())) + input.SetVector(1, testutil.MakeInt64Vector([]int64{0, 20}, []uint64{0}, proc.Mp())) + input.SetVector(2, testutil.MakeInt64Vector([]int64{1, 1}, nil, proc.Mp())) + input.SetRowCount(2) + arg := &Fill{ColLen: 2, FillType: plan.Node_NEXT, PartitionColIdx: []int32{2}, SpillThreshold: 2} + arg.AppendChild(colexec.NewMockOperator().WithBatchs([]*batch.Batch{input})) + require.NoError(t, arg.Prepare(proc)) + + result, err := arg.Call(proc) + require.NoError(t, err) + require.NotNil(t, result.Batch) + require.NotNil(t, arg.ctr.spill) + require.NotNil(t, arg.ctr.spill.next) + require.True(t, arg.ctr.spill.hasSuffix) + + arg.Reset(proc, false, nil) + require.Nil(t, arg.ctr.spill) + arg.Free(proc, false, nil) + input.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} diff --git a/pkg/sql/colexec/fill/types.go b/pkg/sql/colexec/fill/types.go index ab27a33d3bf7b..a2eb09a000636 100644 --- a/pkg/sql/colexec/fill/types.go +++ b/pkg/sql/colexec/fill/types.go @@ -27,13 +27,12 @@ import ( var _ vm.Operator = new(Fill) -const ( - receiveBat = 0 - findNull = 2 - findValue = 3 - fillValue = 4 - findNullPre = 5 -) +// fillCoord addresses one buffered row by the batch's absolute sequence number +// (stable across FIFO popping) and its row within that batch. +type fillCoord struct { + seq int + row int +} type container struct { @@ -42,26 +41,60 @@ type container struct { // prev prevVecs []*vector.Vector + // prevValid marks which prevVecs hold a value from the current partition. + // A partition boundary invalidates them without freeing the vectors. + prevValid []bool + // prevPartKey / prevPartNull snapshot the partition key of the last row of + // the previous batch, so the first row of the next batch can detect a + // boundary without keeping the old batch alive. + prevPartKey [][]byte + prevPartNull []bool + prevPartSet bool - // next + // next / linear incremental engine. bats is a FIFO of still-pending child + // batches; baseSeq is the absolute sequence number of bats[0], so a + // fillCoord captured as an absolute seq stays valid after the FIFO pops its + // head (local index = seq - baseSeq). toFree holds the batch handed to the + // caller on the previous Call, released at the top of the next one. + // flushable counts the resolved prefix of bats that may be emitted; + // childDone records child EOF. bats []*batch.Batch - preIdx int - preRow int - curIdx int - curRow int - status int - subStatus int - idx int - buf *batch.Batch - i int - doneIdx []int - endBatch []bool + baseSeq int + toFree *batch.Batch + flushable int + childDone bool + // pendingBytes accounts for duplicated batches retained in bats. Once it + // crosses spillThreshold while no prefix is flushable, spill owns the + // unresolved suffix and keeps only one batch resident at a time. + pendingBytes int64 + pendingRows int64 + spillThreshold int64 + spill *fillSpill + // next: per fill-column list of NULL rows still waiting for a following + // value of the same partition. + nextRun [][]fillCoord + // linear: linPre is the last non-NULL row per column (seq < 0 means none in + // the current partition), linRun the NULL run waiting to be interpolated + // between linPre and the next non-NULL. + linPre []fillCoord + linRun [][]fillCoord + // linSeed carries the last original non-NULL value across a completed spill + // segment without pinning the segment's final output batch in memory. + linSeed []*vector.Vector + linSeedValid []bool + // linEntry is the endpoint immediately before bats[0]. Unlike linSeed, + // which follows the currently consumed partition and may be cleared when a + // right endpoint arrives, linEntry advances only when a resolved batch is + // emitted. A spill therefore always starts with the endpoint that belongs + // to the beginning of its persisted suffix. + linEntry []*vector.Vector + linEntryValid []bool + linEntryPart spillPartitionSnapshot + + buf *batch.Batch // linear - nullIdx int - nullRow int - exes []colexec.ExpressionExecutor - done bool + exes []colexec.ExpressionExecutor process func(ctr *container, ap *Fill, proc *process.Process, anal process.Analyzer) (vm.CallResult, error) } @@ -72,7 +105,14 @@ type Fill struct { ColLen int FillType plan.Node_FillType FillVal []*plan.Expr - AggIds []int32 + // SpillThreshold follows the shared colexec convention: zero selects the + // CN-local default, small positive values are row-oriented test thresholds, + // and larger values are bytes. + SpillThreshold int64 + // PartitionColIdx locates the time window's partition keys inside the + // input batch. fill(prev/next/linear) treats a change in these columns as + // a hard boundary: values never cross it in either direction. + PartitionColIdx []int32 vm.OperatorBase colexec.Projection @@ -111,6 +151,9 @@ func (fill *Fill) Release() { func (fill *Fill) Reset(proc *process.Process, pipelineFailed bool, err error) { ctr := &fill.ctr + ctr.cleanupSpill(proc) + ctr.clearLinearSeeds(proc.Mp()) + ctr.clearLinearEntries(proc.Mp()) ctr.resetCtrParma() ctr.resetExes() if ctr.buf != nil { @@ -122,6 +165,11 @@ func (fill *Fill) Reset(proc *process.Process, pipelineFailed bool, err error) { } } ctr.bats = ctr.bats[:0] + // toFree was popped out of bats, so the loop above does not cover it. + if ctr.toFree != nil { + ctr.toFree.Clean(proc.GetMPool()) + ctr.toFree = nil + } if fill.ProjectList != nil { if fill.OpAnalyzer != nil { @@ -133,6 +181,7 @@ func (fill *Fill) Reset(proc *process.Process, pipelineFailed bool, err error) { func (fill *Fill) Free(proc *process.Process, pipelineFailed bool, err error) { ctr := &fill.ctr + ctr.cleanupSpill(proc) ctr.freeBatch(proc.Mp()) ctr.freeExes() ctr.freeVectors(proc.Mp()) @@ -155,6 +204,10 @@ func (ctr *container) freeBatch(mp *mpool.MPool) { b.Clean(mp) } } + if ctr.toFree != nil { + ctr.toFree.Clean(mp) + ctr.toFree = nil + } if ctr.buf != nil { ctr.buf.Clean(mp) ctr.buf = nil @@ -168,6 +221,33 @@ func (ctr *container) freeVectors(mp *mpool.MPool) { } } ctr.prevVecs = nil + ctr.clearLinearSeeds(mp) + ctr.clearLinearEntries(mp) +} + +func (ctr *container) clearLinearSeeds(mp *mpool.MPool) { + for i, vec := range ctr.linSeed { + if vec != nil { + vec.Free(mp) + ctr.linSeed[i] = nil + } + } + for i := range ctr.linSeedValid { + ctr.linSeedValid[i] = false + } +} + +func (ctr *container) clearLinearEntries(mp *mpool.MPool) { + for i, vec := range ctr.linEntry { + if vec != nil { + vec.Free(mp) + ctr.linEntry[i] = nil + } + } + for i := range ctr.linEntryValid { + ctr.linEntryValid[i] = false + } + ctr.linEntryPart = spillPartitionSnapshot{} } func (ctr *container) freeExes() { @@ -188,6 +268,24 @@ func (ctr *container) resetExes() { } func (ctr *container) resetCtrParma() { - ctr.initIndex() - ctr.done = false + ctr.baseSeq = 0 + ctr.flushable = 0 + ctr.childDone = false + ctr.pendingBytes = 0 + ctr.pendingRows = 0 + for i := range ctr.prevValid { + ctr.prevValid[i] = false + } + ctr.prevPartKey = nil + ctr.prevPartNull = nil + ctr.prevPartSet = false + for i := range ctr.nextRun { + ctr.nextRun[i] = ctr.nextRun[i][:0] + } + for i := range ctr.linRun { + ctr.linRun[i] = ctr.linRun[i][:0] + } + for i := range ctr.linPre { + ctr.linPre[i] = fillCoord{seq: -1, row: -1} + } } diff --git a/pkg/sql/colexec/timewin/partition_test.go b/pkg/sql/colexec/timewin/partition_test.go new file mode 100644 index 0000000000000..250fede7bab0e --- /dev/null +++ b/pkg/sql/colexec/timewin/partition_test.go @@ -0,0 +1,583 @@ +// Copyright 2021 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package timewin + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "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/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/colexec" + "github.com/matrixorigin/matrixone/pkg/sql/colexec/aggexec" + "github.com/matrixorigin/matrixone/pkg/sql/plan/function" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/vm" + "github.com/matrixorigin/matrixone/pkg/vm/process" + "github.com/stretchr/testify/require" +) + +// nullPart stands in for a NULL partition key in the decoded output. +const nullPart = int64(-9999) + +// row is one input tuple: a timestamp, the value to aggregate, and the +// partition key. Input must already be ordered by (part, ts), which is what +// the planner's ORDER BY guarantees. +type row struct { + ts string + val int32 + part int64 +} + +func mustDatetime(t *testing.T, s string) types.Datetime { + t.Helper() + d, err := types.ParseDatetime(s, 0) + require.NoError(t, err) + return d +} + +// makePartInput builds a batch shaped (ts datetime, val int32, part int64). +func makePartInput(t *testing.T, mp *mpool.MPool, rows []row) *batch.Batch { + t.Helper() + ts := make([]string, 0, len(rows)) + vals := make([]int32, 0, len(rows)) + parts := make([]int64, 0, len(rows)) + for _, r := range rows { + ts = append(ts, r.ts) + vals = append(vals, r.val) + parts = append(parts, r.part) + } + + bat := batch.New([]string{"ts", "val", "part"}) + bat.Vecs[0] = testutil.NewVector(len(rows), types.T_datetime.ToType(), mp, false, ts) + bat.Vecs[1] = testutil.NewVector(len(rows), types.T_int32.ToType(), mp, false, vals) + bat.Vecs[2] = testutil.NewVector(len(rows), types.T_int64.ToType(), mp, false, parts) + bat.SetRowCount(len(rows)) + return bat +} + +func newPartArg(t *testing.T, proc *process.Process, sliding types.Datetime, withPartition bool) *TimeWin { + t.Helper() + arg := &TimeWin{ + WStart: true, + WEnd: false, + Types: []types.Type{types.T_int32.ToType()}, + Aggs: []aggexec.AggFuncExecExpression{ + aggexec.MakeAggFunctionExpression( + function.AggSumOverloadID, false, []*plan.Expr{newExpression(1)}, nil), + }, + TsType: plan.Type{Id: int32(types.T_datetime)}, + Ts: newExpression(0), + Interval: makeInterval(), + Sliding: sliding, + } + if withPartition { + partExpr := newExpression(2) + partExpr.Typ = plan.Type{Id: int32(types.T_int64)} + arg.PartitionBy = []*plan.Expr{partExpr} + } + return arg +} + +// runPartArg drives the operator to exhaustion and returns (wstart, max, part) +// per output row. +func runPartArg(t *testing.T, arg *TimeWin, proc *process.Process, in *batch.Batch) (starts []types.Datetime, sums []int64, parts []int64) { + t.Helper() + op := colexec.NewMockOperator().WithBatchs([]*batch.Batch{in}) + arg.Children = nil + arg.AppendChild(op) + require.NoError(t, arg.Prepare(proc)) + + for { + res, err := vm.Exec(arg, proc) + require.NoError(t, err) + if res.Batch == nil || res.Status == vm.ExecStop { + if res.Batch == nil { + break + } + } + bat := res.Batch + n := bat.Vecs[0].Length() + // Layout: [max, _wstart, part...] + sums = append(sums, vector.MustFixedColNoTypeCheck[int64](bat.Vecs[0])[:n]...) + starts = append(starts, vector.MustFixedColNoTypeCheck[types.Datetime](bat.Vecs[1])[:n]...) + if len(arg.PartitionBy) > 0 { + pv := bat.Vecs[2] + for i := 0; i < n; i++ { + switch { + case pv.IsConstNull(): + // A null partition key broadcasts as a const-null vector, + // which carries no data to index. + parts = append(parts, nullPart) + case pv.IsConst(): + parts = append(parts, vector.MustFixedColNoTypeCheck[int64](pv)[0]) + default: + parts = append(parts, vector.MustFixedColNoTypeCheck[int64](pv)[i]) + } + } + } + if res.Status == vm.ExecStop { + break + } + } + return +} + +// Sliding windows carry state across rows, so a partition boundary has to +// restart that state. Each partition must produce exactly the windows it would +// have produced on its own. +func TestTimeWinSlidingPartitionResetsWindowState(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + sliding, err := calcDatetime(5, 2) // 5 seconds, interval is 5s => tumbling-by-slide + require.NoError(t, err) + + in := makePartInput(t, proc.Mp(), []row{ + {"2023-08-01 00:00:00", 10, 1}, + {"2023-08-01 00:00:01", 20, 1}, + {"2023-08-01 00:00:06", 30, 1}, + {"2023-08-01 00:00:00", 100, 2}, + {"2023-08-01 00:00:07", 400, 2}, + }) + + arg := newPartArg(t, proc, sliding, true) + starts, sums, parts := runPartArg(t, arg, proc, in) + + require.Equal(t, []types.Datetime{ + mustDatetime(t, "2023-08-01 00:00:00"), + mustDatetime(t, "2023-08-01 00:00:05"), + mustDatetime(t, "2023-08-01 00:00:00"), + mustDatetime(t, "2023-08-01 00:00:05"), + }, starts) + // Partition 1's sum must never absorb partition 2's rows: a leak of + // partition 2's first row would make the first window 130 instead of 30. + require.Equal(t, []int64{30, 30, 100, 400}, sums) + require.Equal(t, []int64{1, 1, 2, 2}, parts) + + arg.Free(proc, false, nil) + in.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// A partition whose last windows are still pending when the boundary arrives +// must emit them before the next partition starts; dropping them silently +// loses rows. +func TestTimeWinPartitionEmitsTrailingWindows(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + sliding, err := calcDatetime(5, 2) + require.NoError(t, err) + + in := makePartInput(t, proc.Mp(), []row{ + // partition 1 spans three windows + {"2023-08-01 00:00:00", 1, 1}, + {"2023-08-01 00:00:06", 2, 1}, + {"2023-08-01 00:00:12", 3, 1}, + // partition 2 has a single row + {"2023-08-01 00:00:00", 9, 2}, + }) + + arg := newPartArg(t, proc, sliding, true) + starts, sums, parts := runPartArg(t, arg, proc, in) + + require.Equal(t, []int64{1, 1, 1, 2}, parts, "partition 1 must not lose its trailing windows") + require.Equal(t, []int64{1, 2, 3, 9}, sums) + require.Len(t, starts, 4) + + arg.Free(proc, false, nil) + in.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// A single partition must behave exactly like the unpartitioned operator. +func TestTimeWinSinglePartitionMatchesNoPartition(t *testing.T) { + rows := []row{ + {"2023-08-01 00:00:00", 10, 7}, + {"2023-08-01 00:00:01", 20, 7}, + {"2023-08-01 00:00:06", 30, 7}, + } + sliding, err := calcDatetime(5, 2) + require.NoError(t, err) + + procA := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + inA := makePartInput(t, procA.Mp(), rows) + argA := newPartArg(t, procA, sliding, true) + startsA, sumsA, partsA := runPartArg(t, argA, procA, inA) + + procB := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + inB := makePartInput(t, procB.Mp(), rows) + argB := newPartArg(t, procB, sliding, false) + startsB, sumsB, partsB := runPartArg(t, argB, procB, inB) + + require.Equal(t, startsB, startsA) + require.Equal(t, sumsB, sumsA) + require.Empty(t, partsB) + require.Equal(t, []int64{7, 7}, partsA) + + argA.Free(procA, false, nil) + inA.Clean(procA.Mp()) + procA.Free() + require.Equal(t, int64(0), procA.Mp().CurrNB()) + + argB.Free(procB, false, nil) + inB.Clean(procB.Mp()) + procB.Free() + require.Equal(t, int64(0), procB.Mp().CurrNB()) +} + +// GROUP BY folds NULL keys into one group, so the window must treat two NULL +// keys as the same partition rather than as a boundary. +func TestTimeWinPartitionNullKeysGroupTogether(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + bat := batch.New([]string{"ts", "val", "part"}) + bat.Vecs[0] = testutil.NewVector(2, types.T_datetime.ToType(), proc.Mp(), false, + []string{"2023-08-01 00:00:00", "2023-08-01 00:00:01"}) + bat.Vecs[1] = testutil.NewVector(2, types.T_int32.ToType(), proc.Mp(), false, []int32{10, 20}) + partVec := testutil.NewVector(2, types.T_int64.ToType(), proc.Mp(), false, []int64{0, 0}) + partVec.GetNulls().Add(0) + partVec.GetNulls().Add(1) + bat.Vecs[2] = partVec + bat.SetRowCount(2) + + sliding, err := calcDatetime(5, 2) + require.NoError(t, err) + arg := newPartArg(t, proc, sliding, true) + _, sums, _ := runPartArg(t, arg, proc, bat) + + // One partition, one window: both rows aggregate together. + require.Equal(t, []int64{30}, sums) + + arg.Free(proc, false, nil) + bat.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestTimeWinAnyNullPartitionKey(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + rows := []row{ + {"2023-08-01 00:00:00", 10, 0}, + {"2023-08-01 00:00:01", 20, 0}, + {"2023-08-01 00:00:06", 30, 0}, + } + in := makePartInput(t, proc.Mp(), rows) + in.Vecs[2].Free(proc.Mp()) + in.Vecs[2] = vector.NewConstNull(types.T_any.ToType(), len(rows), proc.Mp()) + + sliding, err := calcDatetime(5, 2) + require.NoError(t, err) + arg := newPartArg(t, proc, sliding, true) + arg.PartitionBy[0].Typ = plan.Type{Id: int32(types.T_any)} + + _, sums, parts := runPartArg(t, arg, proc, in) + require.Equal(t, []int64{30, 30}, sums) + require.Equal(t, []int64{nullPart, nullPart}, parts) + + arg.Free(proc, false, nil) + in.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +func TestAnyPartitionKeyRejectsNonNullValue(t *testing.T) { + mp := mpool.MustNewZero() + typ := types.T_any.ToType() + src := vector.NewVec(typ) + src.SetLength(1) + dst := vector.NewVec(typ) + + err := getPartitionSetFunction(typ, mp)(dst, src, 0, 1) + require.ErrorContains(t, err, "non-NULL T_any partition key") + + src.Free(mp) + dst.Free(mp) + require.Equal(t, int64(0), mp.CurrNB()) +} + +func TestSamePartition(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + ctr := &container{} + ctr.partExe = make([]colexec.ExpressionExecutor, 1) + + v := testutil.NewVector(3, types.T_int64.ToType(), proc.Mp(), false, []int64{5, 5, 9}) + v.GetNulls().Add(2) + ctr.partVec = [][]*vector.Vector{{v}} + + require.True(t, ctr.samePartition(0, 0, 0, 1), "equal keys are one partition") + require.False(t, ctr.samePartition(0, 0, 0, 2), "a null key differs from a non-null one") + require.True(t, ctr.samePartition(0, 2, 0, 2), "a null key equals itself") + + v.Free(proc.Mp()) + proc.Free() +} + +// Without sliding the operator takes the `interval` path, where the child +// aggregate has already reduced each (partition, window) to a single row and +// the operator only decorates it. The partition key must pass straight through +// rather than be broadcast per window. +func TestTimeWinIntervalPathForwardsPartitionKeys(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + in := makePartInput(t, proc.Mp(), []row{ + {"2023-08-01 00:00:00", 10, 1}, + {"2023-08-01 00:00:06", 30, 1}, + {"2023-08-01 00:00:00", 100, 2}, + }) + + arg := newPartArg(t, proc, 0, true) + // A non-nil EndExpr is what selects the pass-through path. + arg.EndExpr = newExpression(0) + arg.WEnd = true + + op := colexec.NewMockOperator().WithBatchs([]*batch.Batch{in}) + arg.Children = nil + arg.AppendChild(op) + require.NoError(t, arg.Prepare(proc)) + + res, err := vm.Exec(arg, proc) + require.NoError(t, err) + require.NotNil(t, res.Batch) + + // Layout: [sum, _wstart, _wend, part] + require.Len(t, res.Batch.Vecs, 4) + parts := vector.MustFixedColNoTypeCheck[int64](res.Batch.Vecs[3]) + require.Equal(t, []int64{1, 1, 2}, parts[:res.Batch.Vecs[0].Length()], + "each input row keeps its own key on the pass-through path") + + arg.Free(proc, false, nil) + in.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// A Reset/Prepare cycle must be a full generation boundary: the second run +// gets the same output a fresh operator would produce, from cursors and +// buffers restarted at zero, and Free still returns every byte. Before the +// fix, the second run resumed through nextWindow with flushed-out aggregate +// state and crashed in GroupGrow. +func TestTimeWinReuseAfterReset(t *testing.T) { + rows := []row{ + {"2023-08-01 00:00:00", 10, 1}, + {"2023-08-01 00:00:01", 20, 1}, + {"2023-08-01 00:00:06", 30, 1}, + {"2023-08-01 00:00:00", 100, 2}, + {"2023-08-01 00:00:07", 400, 2}, + } + sliding, err := calcDatetime(5, 2) + require.NoError(t, err) + + cases := []struct { + name string + withPartition bool + }{ + {"partitioned sliding", true}, + {"sliding without partitions", false}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + arg := newPartArg(t, proc, sliding, tc.withPartition) + + // The first generation reads two child batches so its buffers hold + // two entries; the second reads one, which must land in entry 0 + // rather than continue after the stale pair. + bats1 := []*batch.Batch{ + makePartInput(t, proc.Mp(), rows[:3]), + makePartInput(t, proc.Mp(), rows[3:]), + } + starts1, sums1, parts1 := runPartArgBats(t, arg, proc, bats1) + require.NotEmpty(t, sums1) + + arg.Reset(proc, false, nil) + + in2 := makePartInput(t, proc.Mp(), rows) + starts2, sums2, parts2 := runPartArg(t, arg, proc, in2) + + require.Equal(t, starts1, starts2) + require.Equal(t, sums1, sums2) + require.Equal(t, parts1, parts2) + + arg.Free(proc, false, nil) + for _, b := range bats1 { + b.Clean(proc.Mp()) + } + in2.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) + }) + } +} + +// The interval pass-through path buffers one vector set per child batch, +// indexed by the same cursor; reuse must restart it at zero as well. +func TestTimeWinIntervalPathReuseAfterReset(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + rows := []row{ + {"2023-08-01 00:00:00", 10, 1}, + {"2023-08-01 00:00:06", 30, 1}, + {"2023-08-01 00:00:00", 100, 2}, + } + + arg := newPartArg(t, proc, 0, true) + // A non-nil EndExpr selects the pass-through path. + arg.EndExpr = newExpression(0) + arg.WEnd = true + + // Layout: [val, _wstart, _wend, part]; the values pass through unreduced. + run := func(in *batch.Batch) (vals []int32, parts []int64) { + op := colexec.NewMockOperator().WithBatchs([]*batch.Batch{in}) + arg.Children = nil + arg.AppendChild(op) + require.NoError(t, arg.Prepare(proc)) + for { + res, err := vm.Exec(arg, proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + n := res.Batch.Vecs[0].Length() + vals = append(vals, vector.MustFixedColNoTypeCheck[int32](res.Batch.Vecs[0])[:n]...) + parts = append(parts, vector.MustFixedColNoTypeCheck[int64](res.Batch.Vecs[3])[:n]...) + if res.Status == vm.ExecStop { + break + } + } + return + } + + in1 := makePartInput(t, proc.Mp(), rows) + vals1, parts1 := run(in1) + require.Equal(t, []int32{10, 30, 100}, vals1) + + arg.Reset(proc, false, nil) + + in2 := makePartInput(t, proc.Mp(), rows) + vals2, parts2 := run(in2) + require.Equal(t, vals1, vals2) + require.Equal(t, parts1, parts2) + + arg.Free(proc, false, nil) + in1.Clean(proc.Mp()) + in2.Clean(proc.Mp()) + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) +} + +// runPartArgBats drives the operator over several child batches; the +// partition state machine must behave identically no matter where the +// batch boundaries fall. +func runPartArgBats(t *testing.T, arg *TimeWin, proc *process.Process, bats []*batch.Batch) (starts []types.Datetime, sums []int64, parts []int64) { + t.Helper() + op := colexec.NewMockOperator().WithBatchs(bats) + arg.Children = nil + arg.AppendChild(op) + require.NoError(t, arg.Prepare(proc)) + + for { + res, err := vm.Exec(arg, proc) + require.NoError(t, err) + if res.Batch == nil { + break + } + bat := res.Batch + n := bat.Vecs[0].Length() + sums = append(sums, vector.MustFixedColNoTypeCheck[int64](bat.Vecs[0])[:n]...) + starts = append(starts, vector.MustFixedColNoTypeCheck[types.Datetime](bat.Vecs[1])[:n]...) + if len(arg.PartitionBy) > 0 { + pv := bat.Vecs[2] + for i := 0; i < n; i++ { + if pv.IsConst() { + parts = append(parts, vector.MustFixedColNoTypeCheck[int64](pv)[0]) + } else { + parts = append(parts, vector.MustFixedColNoTypeCheck[int64](pv)[i]) + } + } + } + if res.Status == vm.ExecStop { + break + } + } + return +} + +// The same rows produce the same windows regardless of how the child chops +// them into batches: one partition spanning two batches, a partition starting +// exactly on a batch's first row, and a boundary in the middle of the second +// batch must all match the single-batch run. +func TestTimeWinPartitionAcrossChildBatches(t *testing.T) { + rows := []row{ + {"2023-08-01 00:00:00", 10, 1}, + {"2023-08-01 00:00:01", 20, 1}, + {"2023-08-01 00:00:06", 30, 1}, + {"2023-08-01 00:00:00", 100, 2}, + {"2023-08-01 00:00:07", 400, 2}, + {"2023-08-01 00:00:02", 7, 3}, + } + splits := []struct { + name string + sizes []int + }{ + {"single batch", []int{6}}, + {"partition 1 spans two batches", []int{2, 4}}, + {"partition 2 starts a new batch", []int{3, 3}}, + {"boundary inside the second batch", []int{3, 2, 1}}, + } + + sliding, err := calcDatetime(5, 2) + require.NoError(t, err) + + var wantStarts []types.Datetime + var wantSums, wantParts []int64 + for _, split := range splits { + sizes := split.sizes + t.Run(split.name, func(t *testing.T) { + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + + var bats []*batch.Batch + offset := 0 + for _, size := range sizes { + bats = append(bats, makePartInput(t, proc.Mp(), rows[offset:offset+size])) + offset += size + } + require.Equal(t, len(rows), offset) + + arg := newPartArg(t, proc, sliding, true) + starts, sums, parts := runPartArgBats(t, arg, proc, bats) + + if wantSums == nil { + // The single-batch run is the reference every split must match. + wantStarts, wantSums, wantParts = starts, sums, parts + require.Equal(t, []int64{30, 30, 100, 400, 7}, sums) + require.Equal(t, []int64{1, 1, 2, 2, 3}, parts) + } else { + require.Equal(t, wantStarts, starts) + require.Equal(t, wantSums, sums) + require.Equal(t, wantParts, parts) + } + + arg.Free(proc, false, nil) + for _, b := range bats { + b.Clean(proc.Mp()) + } + proc.Free() + require.Equal(t, int64(0), proc.Mp().CurrNB()) + }) + } +} diff --git a/pkg/sql/colexec/timewin/timewin.go b/pkg/sql/colexec/timewin/timewin.go index fc4ce8b815f2e..d6f2c4f02223e 100644 --- a/pkg/sql/colexec/timewin/timewin.go +++ b/pkg/sql/colexec/timewin/timewin.go @@ -18,6 +18,8 @@ import ( "bytes" "context" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" @@ -58,12 +60,31 @@ func (timeWin *TimeWin) Prepare(proc *process.Process) (err error) { } } } + } + + // Gated separately from the expression executors: Reset discards the + // aggregate state (it cannot survive a Flush) while keeping the executors, + // so a reused operator arrives here with executors but no aggregates. + if len(ctr.aggs) == 0 { ctr.aggs, err = makeAggExecutors(timeWin, proc, false) if err != nil { return err } } + if len(ctr.partExe) == 0 && len(timeWin.PartitionBy) > 0 { + ctr.partExe = make([]colexec.ExpressionExecutor, len(timeWin.PartitionBy)) + ctr.partSet = make([]func(v, w *vector.Vector, sel int64, length int) error, len(timeWin.PartitionBy)) + for i, expr := range timeWin.PartitionBy { + ctr.partExe[i], err = colexec.NewExpressionExecutor(proc, expr) + if err != nil { + return err + } + ctr.partSet[i] = getPartitionSetFunction( + types.New(types.T(expr.Typ.Id), expr.Typ.Width, expr.Typ.Scale), proc.Mp()) + } + } + if ctr.tsExe == nil { ctr.tsExe, err = colexec.NewExpressionExecutor(proc, timeWin.Ts) if err != nil { @@ -99,6 +120,8 @@ func (timeWin *TimeWin) Prepare(proc *process.Process) (err error) { ctr.tsOid = types.T(timeWin.TsType.Id) ctr.resetParam(timeWin) + // Must match plan.BuildTimeWindowLayout: aggregates, then the boundaries, + // then the partition keys. ctr.colCnt = len(timeWin.Aggs) if timeWin.WStart { ctr.colCnt++ @@ -106,9 +129,29 @@ func (timeWin *TimeWin) Prepare(proc *process.Process) (err error) { if timeWin.WEnd { ctr.colCnt++ } + ctr.colCnt += len(timeWin.PartitionBy) return nil } +func getPartitionSetFunction( + typ types.Type, + mp *mpool.MPool, +) func(v, w *vector.Vector, sel int64, length int) error { + if typ.Oid != types.T_any { + return vector.GetConstSetFunction(typ, mp) + } + + // T_any is the physical type of an untyped NULL literal. It has no value + // representation for GetConstSetFunction to copy, but GROUP BY NULL is a + // valid single partition and its key still occupies an output-layout slot. + return func(v, w *vector.Vector, sel int64, length int) error { + if !w.IsConstNull() && !w.IsNull(uint64(sel)) { + return moerr.NewInternalErrorNoCtx("time window received a non-NULL T_any partition key") + } + return vector.SetConstNull(v, length, mp) + } +} + func (timeWin *TimeWin) Call(proc *process.Process) (vm.CallResult, error) { ctr := &timeWin.ctr var err error @@ -230,9 +273,30 @@ func (timeWin *TimeWin) Call(proc *process.Process) (vm.CallResult, error) { return result, err } - if ctr.last { + switch { + case ctr.last: ctr.status = end - } else { + case ctr.partitionBreak: + // The previous partition is fully emitted. Resume at its first + // foreign row and rebuild the window state from scratch, so + // the new partition's windows are anchored on its own data + // rather than sliding the old partition's state forward. + replacements, err := makeAggExecutors(timeWin, proc, false) + if err != nil { + return result, err + } + ctr.freeAgg() + ctr.aggs = replacements + ctr.curVecIdx = ctr.breakVecIdx + ctr.curRowIdx = ctr.breakRowIdx + ctr.preVecIdx = ctr.breakVecIdx + ctr.preRowIdx = ctr.breakRowIdx + ctr.group = -1 + ctr.withoutFill = true + ctr.partEnd = false + ctr.partitionBreak = false + ctr.status = firstWindow + default: replacements, err := makeAggExecutors(timeWin, proc, true) if err != nil { return result, err @@ -356,6 +420,8 @@ func (ctr *container) nextWindow(t *TimeWin) error { } ctr.group++ } + // See firstWindow: the new window is empty until a row lands in it. + ctr.withoutFill = true return nil } @@ -367,12 +433,22 @@ func (ctr *container) firstWindow(t *TimeWin) error { ctr.nextLeft = ctr.left + t.Sliding ctr.nextRight = ctr.nextLeft + t.Interval + // This row opens the partition every window until the next boundary + // belongs to; fillRows compares against it to spot the change. + ctr.partIdx = ctr.curVecIdx + ctr.partRow = ctr.curRowIdx + for _, ag := range ctr.aggs { if err := ag.GroupGrow(1); err != nil { return err } } ctr.group++ + // The window is empty until fillRows lands a row in it. This must live at + // window switches, not at the top of fillRows: a window's rows can arrive + // in one batch and its closing row in the next, and resetting the flag + // per batch made such a window look empty and drop from the output. + ctr.withoutFill = true return nil } @@ -381,8 +457,24 @@ func (ctr *container) fillRows() error { vals := vector.MustFixedColNoTypeCheck[types.Datetime](ctr.tsVec[ctr.curVecIdx]) outRange := false - ctr.withoutFill = true + partBreak := false for ; ctr.curRowIdx < cnt; ctr.curRowIdx++ { + // Input arrives ordered by partition key, so the first row that + // disagrees with the key this window opened on ends the partition's + // data. Its remaining windows still have to be produced, so record + // where to resume and fall through to the same slide-to-exhaustion + // logic that end-of-stream uses. + if len(ctr.partExe) > 0 && !ctr.samePartition(ctr.curVecIdx, ctr.curRowIdx, ctr.partIdx, ctr.partRow) { + ctr.partEnd = true + ctr.breakVecIdx = ctr.curVecIdx + ctr.breakRowIdx = ctr.curRowIdx + partBreak = true + break + } + ctr.partLastVal = vals[ctr.curRowIdx] + ctr.partLastVecIdx = ctr.curVecIdx + ctr.partLastRowIdx = ctr.curRowIdx + if vals[ctr.curRowIdx] <= ctr.nextLeft { ctr.preVecIdx = ctr.curVecIdx ctr.preRowIdx = ctr.curRowIdx @@ -401,7 +493,21 @@ func (ctr *container) fillRows() error { } } - if outRange { + switch { + case partBreak: + // Same shape as the end-of-stream check in nextBatch, scoped to the + // partition: once the rewind point has reached the partition's last + // row and the next window would start past it, the partition is done. + if ctr.preVecIdx == ctr.partLastVecIdx && ctr.preRowIdx == ctr.partLastRowIdx && + ctr.partLastVal < ctr.nextLeft { + ctr.partitionBreak = true + ctr.wStart = append(ctr.wStart, ctr.left) + ctr.wEnd = append(ctr.wEnd, ctr.right) + ctr.status = flush + } else { + ctr.status = nextWindow + } + case outRange: if ctr.group > maxTimeWindowRows { ctr.wStart = append(ctr.wStart, ctr.left) ctr.wEnd = append(ctr.wEnd, ctr.right) @@ -409,7 +515,7 @@ func (ctr *container) fillRows() error { } else { ctr.status = nextWindow } - } else { + default: ctr.lastVal = vals[cnt-1] ctr.status = nextBatch } @@ -419,12 +525,7 @@ func (ctr *container) fillRows() error { const maxTimeWindowRows = 8192 func (ctr *container) calRes(ap *TimeWin, proc *process.Process) (err error) { - // The output batch remains owned by TimeWin and is valid until the next - // Call. Once the downstream asks for another result, the previous batch has - // been consumed and must be released before ctr.bat is replaced. - if ctr.bat != nil { - ctr.bat.Clean(proc.Mp()) - } + ctr.freeFlushedAggVecs(proc.Mp()) ctr.bat = batch.NewWithSize(ctr.colCnt) i := 0 for _, agg := range ctr.aggs { @@ -442,7 +543,16 @@ func (ctr *container) calRes(ap *TimeWin, proc *process.Process) (err error) { } if !ap.WStart && !ap.WEnd { + if err = ctr.setPartVecsForWindows(i, ctr.bat.Vecs[0].Length(), proc); err != nil { + return err + } batch.SetLength(ctr.bat, ctr.bat.Vecs[0].Length()) + // Sliding execution records boundaries while building groups even when + // pruning removed both boundary columns from the output. A flush closes + // that generation of groups, so retaining the unused bounds here would + // grow these slices for the lifetime of the query. + ctr.wStart = nil + ctr.wEnd = nil return nil } bat := batch.NewWithSize(1) @@ -483,6 +593,11 @@ func (ctr *container) calRes(ap *TimeWin, proc *process.Process) (err error) { if err != nil { return err } + i++ + } + + if err = ctr.setPartVecsForWindows(i, ctr.bat.Vecs[0].Length(), proc); err != nil { + return err } batch.SetLength(ctr.bat, ctr.bat.Vecs[0].Length()) @@ -500,6 +615,9 @@ func (ctr *container) calResForInterval(ap *TimeWin, proc *process.Process) (err } if !ap.WStart && !ap.WEnd { + // The partition keys still have to land in their slots, and they sit + // after the (absent) boundaries. + ctr.setPartVecsForInterval(i) batch.SetLength(ctr.bat, ctr.bat.Vecs[0].Length()) return nil } @@ -521,14 +639,78 @@ func (ctr *container) calResForInterval(ap *TimeWin, proc *process.Process) (err if err != nil { return err } + i++ } + ctr.setPartVecsForInterval(i) + batch.SetLength(ctr.bat, ctr.bat.Vecs[0].Length()) ctr.wStart = nil ctr.wEnd = nil return nil } +// setPartVecsForInterval forwards the partition keys row-for-row. Without +// sliding the child aggregate has already reduced each (partition, window) to +// one row, so this path never merges rows and the key passes straight through. +func (ctr *container) setPartVecsForInterval(slot int) { + if len(ctr.partExe) == 0 { + // Without partition keys evalPartVector buffers nothing, so partVec + // has no entry for this batch to read. + return + } + for _, vec := range ctr.partVec[ctr.i-1] { + ctr.bat.SetVector(int32(slot), vec) + slot++ + } +} + +// freeFlushedAggVecs releases the aggregate vectors of the previously flushed +// result. Each flush replaces ctr.bat, and the Flush() results are the only +// vectors that batch owns outright -- the boundaries belong to their expression +// executors and the partition keys to ctr.partOut. Without this, every flush +// after the first orphans a set of aggregate vectors, which partitioning makes +// routine rather than rare. +func (ctr *container) freeFlushedAggVecs(mp *mpool.MPool) { + if ctr.bat == nil { + return + } + for i := 0; i < len(ctr.aggs) && i < len(ctr.bat.Vecs); i++ { + if vec := ctr.bat.Vecs[i]; vec != nil { + vec.Free(mp) + ctr.bat.SetVector(int32(i), nil) + } + } +} + +// setPartVecsForWindows broadcasts the current partition's key across the rows +// being flushed. Sliding windows collapse many input rows into one row per +// window, but a flush never spans partitions, so one key covers the batch. +func (ctr *container) setPartVecsForWindows(slot, length int, proc *process.Process) error { + if len(ctr.partExe) == 0 { + return nil + } + if ctr.partIdx < 0 { + return moerr.NewInternalErrorNoCtx("time window flushed a result before any partition key was seen") + } + if len(ctr.partOut) == 0 { + ctr.partOut = make([]*vector.Vector, len(ctr.partExe)) + } + for p, src := range ctr.partVec[ctr.partIdx] { + if ctr.partOut[p] == nil { + ctr.partOut[p] = vector.NewVec(*src.GetType()) + } else { + ctr.partOut[p].CleanOnlyData() + } + if err := ctr.partSet[p](ctr.partOut[p], src, int64(ctr.partRow), length); err != nil { + return err + } + ctr.bat.SetVector(int32(slot), ctr.partOut[p]) + slot++ + } + return nil +} + func (ctr *container) evalVector(bat *batch.Batch, proc *process.Process) error { if err := ctr.evalTsVector(bat, proc); err != nil { return err @@ -536,10 +718,60 @@ func (ctr *container) evalVector(bat *batch.Batch, proc *process.Process) error if err := ctr.evalAggVector(bat, proc); err != nil { return err } + if err := ctr.evalPartVector(bat, proc); err != nil { + return err + } ctr.i++ return nil } +func (ctr *container) evalPartVector(bat *batch.Batch, proc *process.Process) error { + if len(ctr.partExe) == 0 { + return nil + } + f := len(ctr.partVec) > ctr.i + if !f { + ctr.partVec = append(ctr.partVec, make([]*vector.Vector, len(ctr.partExe))) + } + for i := range ctr.partExe { + vec, err := ctr.partExe[i].Eval(proc, []*batch.Batch{bat}, nil) + if err != nil { + return err + } + if f { + ctr.partVec[ctr.i][i].CleanOnlyData() + if err = ctr.partVec[ctr.i][i].UnionBatch(vec, 0, vec.Length(), nil, proc.Mp()); err != nil { + return err + } + } else { + ctr.partVec[ctr.i][i], err = vec.Dup(proc.Mp()) + if err != nil { + return err + } + } + } + return nil +} + +// samePartition reports whether two buffered rows carry the same partition key. +func (ctr *container) samePartition(vecIdx1, rowIdx1, vecIdx2, rowIdx2 int) bool { + for i := range ctr.partExe { + v1, v2 := ctr.partVec[vecIdx1][i], ctr.partVec[vecIdx2][i] + null1, null2 := v1.IsNull(uint64(rowIdx1)), v2.IsNull(uint64(rowIdx2)) + if null1 || null2 { + // GROUP BY folds NULLs together, so the window must too. + if null1 != null2 { + return false + } + continue + } + if !bytes.Equal(v1.GetRawBytesAt(rowIdx1), v2.GetRawBytesAt(rowIdx2)) { + return false + } + } + return true +} + func (ctr *container) evalTsVector(bat *batch.Batch, proc *process.Process) error { vec, err := ctr.tsExe.Eval(proc, []*batch.Batch{bat}, nil) if err != nil { diff --git a/pkg/sql/colexec/timewin/timewin_test.go b/pkg/sql/colexec/timewin/timewin_test.go index 20b085ce311b3..c0441443f69a2 100644 --- a/pkg/sql/colexec/timewin/timewin_test.go +++ b/pkg/sql/colexec/timewin/timewin_test.go @@ -191,9 +191,13 @@ func TestTimeWinSplitDistinctResultAndReplace(t *testing.T) { arg.ctr.status = flush arg.ctr.colCnt = 1 arg.ctr.aggs = []aggexec.AggFuncExec{agg} + arg.ctr.wStart = make([]types.Datetime, rows) + arg.ctr.wEnd = make([]types.Datetime, rows) result, err := arg.Call(proc) require.NoError(t, err) require.NotNil(t, result.Batch) + require.Empty(t, arg.ctr.wStart) + require.Empty(t, arg.ctr.wEnd) resultValues := vector.MustFixedColWithTypeCheck[int64](result.Batch.Vecs[0]) require.Len(t, resultValues, rows) for _, idx := range []int{0, aggexec.AggBatchSize - 1, aggexec.AggBatchSize, rows - 1} { @@ -209,10 +213,14 @@ func TestTimeWinSplitDistinctResultAndReplace(t *testing.T) { // released when the second replacement is installed. require.NoError(t, arg.ctr.aggs[0].Fill(0, 0, []*vector.Vector{input})) arg.ctr.status = flush + arg.ctr.wStart = make([]types.Datetime, maxTimeWindowRows+1) + arg.ctr.wEnd = make([]types.Datetime, maxTimeWindowRows+1) secondResult, err := arg.Call(proc) require.NoError(t, err) require.NotNil(t, secondResult.Batch) require.Equal(t, []int64{1}, vector.MustFixedColWithTypeCheck[int64](secondResult.Batch.Vecs[0])) + require.Empty(t, arg.ctr.wStart) + require.Empty(t, arg.ctr.wEnd) arg.Free(proc, false, nil) input.Free(proc.Mp()) diff --git a/pkg/sql/colexec/timewin/types.go b/pkg/sql/colexec/timewin/types.go index fa24a378c15cd..9ff885085f6d1 100644 --- a/pkg/sql/colexec/timewin/types.go +++ b/pkg/sql/colexec/timewin/types.go @@ -49,6 +49,14 @@ type container struct { aggExe []colexec.ExpressionExecutor aggVec [][]*vector.Vector + partExe []colexec.ExpressionExecutor + partVec [][]*vector.Vector + // partSet broadcasts one input row's partition key across a whole flushed + // batch: every window in a flush belongs to a single partition, because + // the operator flushes at each boundary. + partSet []func(v, w *vector.Vector, sel int64, length int) error + partOut []*vector.Vector + tsExe colexec.ExpressionExecutor tsVec []*vector.Vector tsOid types.T @@ -83,6 +91,26 @@ type container struct { last bool lastVal types.Datetime + + // partIdx / partRow locate the row whose partition key the window + // currently being accumulated belongs to; -1 before the first window. + partIdx int + partRow int + // partEnd mirrors `end` but for one partition: its rows are exhausted, so + // windows keep sliding until they pass the partition's last value. + partEnd bool + // breakVecIdx / breakRowIdx hold the first row of the next partition, the + // row the restart re-anchors on. + breakVecIdx int + breakRowIdx int + // partLast* track the last row seen inside the current partition, playing + // the role `lastVal` and the final buffered row play for the whole stream. + partLastVal types.Datetime + partLastVecIdx int + partLastRowIdx int + // partitionBreak marks that the pending flush ends a partition, so the + // next window must restart rather than slide. + partitionBreak bool } type TimeWin struct { @@ -91,6 +119,11 @@ type TimeWin struct { Types []types.Type Aggs []aggexec.AggFuncExecExpression + // PartitionBy holds the GROUP BY keys other than the window's timestamp. + // Each distinct key value gets its own window sequence, so input must + // arrive ordered by these keys first. + PartitionBy []*plan.Expr + TsType plan.Type Ts *plan.Expr EndExpr *plan.Expr @@ -138,6 +171,20 @@ func (timeWin *TimeWin) Release() { func (timeWin *TimeWin) Reset(proc *process.Process, pipelineFailed bool, err error) { ctr := &timeWin.ctr ctr.resetExes() + // The last flushed batch and the aggregate executors belong to the finished + // generation. In the sliding path the batch owns its aggregate prefix (the + // boundaries belong to their expression executors and the partition keys to + // partOut); in the interval path every vector is a buffer that outlives the + // batch, so only the reference is dropped. Aggregates cannot be rewound + // once Flush has run (see makeAggExecutors), so they are discarded here and + // rebuilt by Prepare. The tsVec/aggVec/partVec buffers stay allocated: with + // the cursors back at zero the next generation reuses them from index 0. + if timeWin.EndExpr == nil { + ctr.freeFlushedAggVecs(proc.Mp()) + } + ctr.bat = nil + ctr.freeAgg() + ctr.aggs = nil ctr.resetParam(timeWin) } @@ -208,6 +255,11 @@ func (ctr *container) resetExes() { exe.ResetForNextQuery() } } + for _, exe := range ctr.partExe { + if exe != nil { + exe.ResetForNextQuery() + } + } if ctr.tsExe != nil { ctr.tsExe.ResetForNextQuery() } @@ -219,16 +271,43 @@ func (ctr *container) resetExes() { } } +// resetParam rewinds every piece of per-generation state, so a Reset/Prepare +// cycle starts from the same blank slate as a fresh operator. Any cursor left +// over from the previous run would either read stale buffered rows or, worse, +// route receive into nextWindow with window bounds that no longer exist. func (ctr *container) resetParam(timeWin *TimeWin) { if timeWin.EndExpr != nil { ctr.status = interval } else { ctr.status = receive } + ctr.i = 0 ctr.end = false ctr.group = -1 ctr.wStart = nil ctr.wEnd = nil + + ctr.curVecIdx = 0 + ctr.curRowIdx = 0 + ctr.preVecIdx = 0 + ctr.preRowIdx = 0 + ctr.left = 0 + ctr.right = 0 + ctr.nextLeft = 0 + ctr.nextRight = 0 + ctr.withoutFill = false + ctr.last = false + ctr.lastVal = 0 + + ctr.partIdx = -1 + ctr.partRow = 0 + ctr.partEnd = false + ctr.breakVecIdx = 0 + ctr.breakRowIdx = 0 + ctr.partLastVal = 0 + ctr.partLastVecIdx = 0 + ctr.partLastRowIdx = 0 + ctr.partitionBreak = false } func (ctr *container) freeExes() { @@ -237,6 +316,11 @@ func (ctr *container) freeExes() { exe.Free() } } + for _, exe := range ctr.partExe { + if exe != nil { + exe.Free() + } + } if ctr.tsExe != nil { ctr.tsExe.Free() } @@ -278,4 +362,36 @@ func (ctr *container) freeVector(mp *mpool.MPool) { } } ctr.aggVec = nil + + for _, vecs := range ctr.partVec { + for _, vec := range vecs { + if vec != nil { + vec.Free(mp) + } + } + } + ctr.partVec = nil + + ctr.freePartOut(mp) + + // calRes only ever hands the *cast results* of these two to the output + // batch; the datetime staging vectors themselves are owned here and were + // never released before. + if ctr.startVec != nil { + ctr.startVec.Free(mp) + ctr.startVec = nil + } + if ctr.endVec != nil { + ctr.endVec.Free(mp) + ctr.endVec = nil + } +} + +func (ctr *container) freePartOut(mp *mpool.MPool) { + for _, vec := range ctr.partOut { + if vec != nil { + vec.Free(mp) + } + } + ctr.partOut = nil } diff --git a/pkg/sql/compile/operator.go b/pkg/sql/compile/operator.go index 78ca821222587..a7c3679aa9983 100644 --- a/pkg/sql/compile/operator.go +++ b/pkg/sql/compile/operator.go @@ -1473,48 +1473,36 @@ func constructUnionAll(_ *plan.Node) *unionall.UnionAll { } func constructFill(node *plan.Node) *fill.Fill { - aggIdx := make([]int32, len(node.AggList)) - for i, expr := range node.AggList { - f := expr.Expr.(*plan.Expr_F) - obj := int64(uint64(f.F.Func.Obj) & function.DistinctMask) - aggIdx[i], _ = function.DecodeOverloadID(obj) - } arg := fill.NewArgument() + // AggList is pruned in lockstep with the child TIME_WINDOW's aggregates, + // so this stays aligned with the prefix that child projects. arg.ColLen = len(node.AggList) arg.FillType = node.FillType arg.FillVal = node.FillVal - arg.AggIds = aggIdx + arg.PartitionColIdx = node.TimeWindowPartitionColPos return arg } func constructTimeWindow(_ context.Context, node *plan.Node, proc *process.Process) *timewin.TimeWin { - var aggregationExpressions []aggexec.AggFuncExecExpression = nil - var typs []types.Type - var wStart, wEnd bool - i := 0 - for _, expr := range node.AggList { - if e, ok := expr.Expr.(*plan.Expr_Col); ok { - if e.Col.Name == plan2.TimeWindowStart { - wStart = true - } - if e.Col.Name == plan2.TimeWindowEnd { - wEnd = true - } - continue - } - f := expr.Expr.(*plan.Expr_F) + // The planner addresses this operator's output through the same layout, + // so derive both from BuildTimeWindowLayout rather than re-deriving here. + layout := plan2.BuildTimeWindowLayout(node) + aggregationExpressions := make([]aggexec.AggFuncExecExpression, 0, len(layout.AggIdx)) + typs := make([]types.Type, 0, len(layout.AggIdx)) + for _, aggIdx := range layout.AggIdx { + f := node.AggList[aggIdx].Expr.(*plan.Expr_F) isDistinct := (uint64(f.F.Func.Obj) & function.Distinct) != 0 functionID := int64(uint64(f.F.Func.Obj) & function.DistinctMask) + // Every slot the layout hands out must get an aggregate, or the + // operator's columns stop matching the positions the planner projects. e := f.F.Args[0] - if e != nil { - aggregationExpressions = append( - aggregationExpressions, - aggexec.MakeAggFunctionExpression(functionID, isDistinct, f.F.Args, nil)) - - typs = append(typs, types.New(types.T(e.Typ.Id), e.Typ.Width, e.Typ.Scale)) - } - i++ + aggregationExpressions = append( + aggregationExpressions, + aggexec.MakeAggFunctionExpression(functionID, isDistinct, f.F.Args, nil)) + typs = append(typs, types.New(types.T(e.Typ.Id), e.Typ.Width, e.Typ.Scale)) } + wStart := layout.WStartSlot != plan2.TimeWindowSlotNone + wEnd := layout.WEndSlot != plan2.TimeWindowSlotNone arg := timewin.NewArgument() err := arg.MakeIntervalAndSliding(node.Interval, node.Sliding) @@ -1524,13 +1512,43 @@ func constructTimeWindow(_ context.Context, node *plan.Node, proc *process.Proce arg.Types = typs arg.Aggs = aggregationExpressions arg.Ts = node.GroupBy[0] + arg.PartitionBy = node.TimeWindowPartitionBy arg.WStart = wStart arg.WEnd = wEnd - arg.EndExpr = node.WEnd + // The operator evaluates the window-end expression against a batch holding + // only the timestamp, so its column reference has to name slot 0. The + // planner leaves it pointing at the timestamp's GROUP BY position, which is + // 0 only while the window key is the sole grouping key. Copy before + // rewriting: the plan may be reused. + if node.WEnd != nil { + endExpr := plan2.DeepCopyExpr(node.WEnd) + resetTimeWindowTsColRef(endExpr) + arg.EndExpr = endExpr + } arg.TsType = node.Timestamp.Typ return arg } +// resetTimeWindowTsColRef points every column reference in a time-window +// helper expression at slot 0, the single timestamp column the operator feeds +// it. The expression is derived from the window's timestamp, so it can hold no +// other column. +func resetTimeWindowTsColRef(expr *plan.Expr) { + switch e := expr.Expr.(type) { + case *plan.Expr_Col: + e.Col.RelPos = 0 + e.Col.ColPos = 0 + case *plan.Expr_F: + for _, arg := range e.F.Args { + resetTimeWindowTsColRef(arg) + } + case *plan.Expr_List: + for _, item := range e.List.List { + resetTimeWindowTsColRef(item) + } + } +} + func constructWindow(_ context.Context, node *plan.Node, proc *process.Process) *window.Window { aggregationExpressions := make([]aggexec.AggFuncExecExpression, len(node.WinSpecList)) diff --git a/pkg/sql/plan/deepcopy.go b/pkg/sql/plan/deepcopy.go index 27bab76f99dd4..af8e7b15bd6e1 100644 --- a/pkg/sql/plan/deepcopy.go +++ b/pkg/sql/plan/deepcopy.go @@ -196,23 +196,33 @@ func DeepCopyRankOption(opt *plan.RankOption) *plan.RankOption { func DeepCopyNode(node *plan.Node) *plan.Node { newNode := &Node{ - NodeType: node.NodeType, - NodeId: node.NodeId, - ExtraOptions: node.ExtraOptions, - Children: slices.Clone(node.Children), - JoinType: node.JoinType, - IsRightJoin: node.IsRightJoin, - BindingTags: slices.Clone(node.BindingTags), - Limit: DeepCopyExpr(node.Limit), - Offset: DeepCopyExpr(node.Offset), - ProjectList: DeepCopyExprList(node.ProjectList), - OnList: DeepCopyExprList(node.OnList), - FilterList: DeepCopyExprList(node.FilterList), - BlockFilterList: DeepCopyExprList(node.BlockFilterList), - GroupBy: DeepCopyExprList(node.GroupBy), - GroupingFlag: slices.Clone(node.GroupingFlag), - AggList: DeepCopyExprList(node.AggList), - OrderBy: DeepCopyOrderBySpecList(node.OrderBy), + NodeType: node.NodeType, + NodeId: node.NodeId, + ExtraOptions: node.ExtraOptions, + Children: slices.Clone(node.Children), + JoinType: node.JoinType, + IsRightJoin: node.IsRightJoin, + BindingTags: slices.Clone(node.BindingTags), + Limit: DeepCopyExpr(node.Limit), + Offset: DeepCopyExpr(node.Offset), + ProjectList: DeepCopyExprList(node.ProjectList), + OnList: DeepCopyExprList(node.OnList), + FilterList: DeepCopyExprList(node.FilterList), + BlockFilterList: DeepCopyExprList(node.BlockFilterList), + GroupBy: DeepCopyExprList(node.GroupBy), + GroupingFlag: slices.Clone(node.GroupingFlag), + AggList: DeepCopyExprList(node.AggList), + OrderBy: DeepCopyOrderBySpecList(node.OrderBy), + Interval: DeepCopyExpr(node.Interval), + Sliding: DeepCopyExpr(node.Sliding), + Timestamp: DeepCopyExpr(node.Timestamp), + WEnd: DeepCopyExpr(node.WEnd), + FillType: node.FillType, + FillVal: DeepCopyExprList(node.FillVal), + + TimeWindowPartitionBy: DeepCopyExprList(node.TimeWindowPartitionBy), + TimeWindowPartitionColPos: slices.Clone(node.TimeWindowPartitionColPos), + DeleteCtx: DeepCopyDeleteCtx(node.DeleteCtx), TblFuncExprList: DeepCopyExprList(node.TblFuncExprList), ClusterTable: DeepCopyClusterTable(node.GetClusterTable()), diff --git a/pkg/sql/plan/deepcopy_time_window_test.go b/pkg/sql/plan/deepcopy_time_window_test.go new file mode 100644 index 0000000000000..53e5fc64e5e09 --- /dev/null +++ b/pkg/sql/plan/deepcopy_time_window_test.go @@ -0,0 +1,117 @@ +// Copyright 2021 - 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/stretchr/testify/require" +) + +func twTestColExpr(relPos, colPos int32) *plan.Expr { + return &plan.Expr{ + Typ: plan.Type{Id: 1}, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{RelPos: relPos, ColPos: colPos}, + }, + } +} + +// DeepCopyNode historically skipped every TIME_WINDOW/FILL field, so copying a +// subtree silently dropped the window definition and partition metadata. This +// pins the round-trip: all fields survive, and mutating the copy cannot reach +// the original. +func TestDeepCopyNodeTimeWindowFields(t *testing.T) { + node := &plan.Node{ + NodeType: plan.Node_TIME_WINDOW, + Interval: twTestColExpr(1, 0), + Sliding: twTestColExpr(1, 1), + Timestamp: twTestColExpr(1, 2), + WEnd: twTestColExpr(1, 3), + FillType: plan.Node_PREV, + FillVal: []*plan.Expr{twTestColExpr(2, 0)}, + TimeWindowPartitionBy: []*plan.Expr{twTestColExpr(3, 0), twTestColExpr(3, 1)}, + } + + copied := DeepCopyNode(node) + + require.Equal(t, node.Interval, copied.Interval) + require.Equal(t, node.Sliding, copied.Sliding) + require.Equal(t, node.Timestamp, copied.Timestamp) + require.Equal(t, node.WEnd, copied.WEnd) + require.Equal(t, node.FillType, copied.FillType) + require.Equal(t, node.FillVal, copied.FillVal) + require.Equal(t, node.TimeWindowPartitionBy, copied.TimeWindowPartitionBy) + + // Deep, not shallow: the copy must not alias the original's expressions. + copied.TimeWindowPartitionBy[0].GetCol().ColPos = 99 + copied.WEnd.GetCol().ColPos = 99 + copied.FillVal[0].GetCol().ColPos = 99 + require.Equal(t, int32(0), node.TimeWindowPartitionBy[0].GetCol().ColPos) + require.Equal(t, int32(3), node.WEnd.GetCol().ColPos) + require.Equal(t, int32(0), node.FillVal[0].GetCol().ColPos) +} + +// twMarkVisitRule flags every expression it is offered, so a test can prove a +// node field is (or is not) reachable from the plan visitor. +type twMarkVisitRule struct { + seen map[*plan.Expr]bool +} + +func (r *twMarkVisitRule) MatchNode(*Node) bool { return false } +func (r *twMarkVisitRule) IsApplyExpr() bool { return true } +func (r *twMarkVisitRule) ApplyNode(*Node) error { return nil } +func (r *twMarkVisitRule) ApplyExpr(e *Expr) (*Expr, error) { + r.seen[e] = true + return e, nil +} + +// The plan visitor rewrites expressions in place (parameter binding, constant +// folding); a partition key it cannot reach would keep stale references. +func TestVisitPlanReachesTimeWindowPartitionBy(t *testing.T) { + partExpr := twTestColExpr(3, 0) + node := &plan.Node{ + NodeType: plan.Node_TIME_WINDOW, + TimeWindowPartitionBy: []*plan.Expr{partExpr}, + } + pl := &Plan{Plan: &plan.Plan_Query{Query: &Query{ + Steps: []int32{0}, + Nodes: []*plan.Node{node}, + }}} + + rule := &twMarkVisitRule{seen: make(map[*plan.Expr]bool)} + require.NoError(t, NewVisitPlan(pl, []VisitPlanRule{rule}).Visit(t.Context())) + require.True(t, rule.seen[partExpr], "partition keys must be visited") +} + +// replaceColumnsForNode rewrites column references when a projection is +// inlined; partition keys decide row grouping, so missing them would leave +// stale references behind. +func TestReplaceColumnsForNodeTimeWindowPartitionBy(t *testing.T) { + node := &plan.Node{ + NodeType: plan.Node_TIME_WINDOW, + TimeWindowPartitionBy: []*plan.Expr{twTestColExpr(5, 2)}, + } + projMap := map[[2]int32]*plan.Expr{ + {5, 2}: twTestColExpr(7, 4), + } + + replaceColumnsForNode(node, projMap) + + col := node.TimeWindowPartitionBy[0].GetCol() + require.Equal(t, int32(7), col.RelPos) + require.Equal(t, int32(4), col.ColPos) +} diff --git a/pkg/sql/plan/explain/time_window_prune_test.go b/pkg/sql/plan/explain/time_window_prune_test.go new file mode 100644 index 0000000000000..a96c3179c16fa --- /dev/null +++ b/pkg/sql/plan/explain/time_window_prune_test.go @@ -0,0 +1,258 @@ +// Copyright 2021 - 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package explain + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/pb/plan" + plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan" + "github.com/stretchr/testify/require" +) + +// timeWindowNode returns the single TIME_WINDOW node of a built plan. +func timeWindowNode(t *testing.T, sql string) *plan.Node { + t.Helper() + logicPlan, err := buildOneStmt(plan2.NewMockOptimizer(false), t, sql) + require.NoError(t, err) + + var found *plan.Node + for _, node := range reachablePlanNodes(logicPlan.GetQuery()) { + if node.NodeType == plan.Node_TIME_WINDOW { + require.Nil(t, found, "expected exactly one TIME_WINDOW node") + found = node + } + } + require.NotNil(t, found) + return found +} + +func fillNode(t *testing.T, sql string) *plan.Node { + t.Helper() + logicPlan, err := buildOneStmt(plan2.NewMockOptimizer(false), t, sql) + require.NoError(t, err) + + for _, node := range reachablePlanNodes(logicPlan.GetQuery()) { + if node.NodeType == plan.Node_FILL { + return node + } + } + t.Fatal("no FILL node found") + return nil +} + +// projectedSlots lists the operator output positions a node's ProjectList +// reads, in projection order. +func projectedSlots(node *plan.Node) []int32 { + slots := make([]int32, 0, len(node.ProjectList)) + for _, expr := range node.ProjectList { + if col := expr.GetCol(); col != nil { + slots = append(slots, col.ColPos) + } + } + return slots +} + +const twTable = "constraint_test.t_on_update" + +// A TIME_WINDOW ProjectList addresses the operator's output batch, which is +// laid out from the (pruned) AggList. Every projected slot must therefore be +// inside the layout the operator will actually build. +func requireSlotsWithinLayout(t *testing.T, node *plan.Node) { + t.Helper() + layout := plan2.BuildTimeWindowLayout(node) + for _, slot := range projectedSlots(node) { + require.Less(t, slot, layout.ColCnt, + "projected slot %d is outside the operator's %d output columns", slot, layout.ColCnt) + } +} + +func TestTimeWindowPruneOperatorShape(t *testing.T) { + t.Run("discarded aggregates are pruned and slots compact", func(t *testing.T) { + // Only min(val) survives, so it must move to slot 0 rather than keep + // max(val)'s original position. Reading slot 0 while max(val) still + // occupied it returned max's values under the min alias. + node := timeWindowNode(t, + "select c from (select _wstart as a, max(val) as b, min(val) as c from "+twTable+ + " interval(updated_at, 5, second)) x") + + require.Len(t, node.AggList, 1) + require.Equal(t, "min", node.AggList[0].GetF().Func.ObjName) + require.Equal(t, []int32{0}, projectedSlots(node)) + requireSlotsWithinLayout(t, node) + }) + + t.Run("boundary output survives when aggregates are pruned", func(t *testing.T) { + // _wstart is the only consumed output; it must be addressed at the + // boundary slot, not at slot 0 where an aggregate used to sit. + node := timeWindowNode(t, + "select a from (select _wstart as a, max(val) as b, min(val) as c from "+twTable+ + " interval(updated_at, 5, second)) x") + + require.Len(t, node.AggList, 1) + layout := plan2.BuildTimeWindowLayout(node) + require.Equal(t, []int32{layout.WStartSlot}, projectedSlots(node)) + requireSlotsWithinLayout(t, node) + }) + + t.Run("wend survives when wstart is pruned", func(t *testing.T) { + // Pruning _wstart must not abandon the rest of the remapping: _wend + // used to be skipped entirely, leaving the parent unable to resolve it. + node := timeWindowNode(t, + "select d from (select _wstart as a, _wend as d, max(val) as b from "+twTable+ + " interval(updated_at, 5, second)) x") + + require.Len(t, node.AggList, 1) + layout := plan2.BuildTimeWindowLayout(node) + require.Equal(t, plan2.TimeWindowSlotNone, layout.WStartSlot) + require.Equal(t, []int32{layout.WEndSlot}, projectedSlots(node)) + requireSlotsWithinLayout(t, node) + }) + + t.Run("aggregates stay a prefix ahead of the boundaries", func(t *testing.T) { + node := timeWindowNode(t, + "select _wstart, _wend, max(val) from "+twTable+ + " interval(updated_at, 10, minute) sliding(5, minute)") + + // Projection order must be [aggregate, _wstart, _wend]: FILL and the + // operator both read this batch positionally. + require.Equal(t, []int32{0, 1, 2}, projectedSlots(node)) + requireSlotsWithinLayout(t, node) + }) + + t.Run("everything discarded keeps a boundary row carrier", func(t *testing.T) { + // The window count is still the row count, and the operator sizes its + // batch off Vecs[0], so one column has to survive -- preferably the + // boundary, which runs no aggregate. + node := timeWindowNode(t, + "select 1 from (select _wstart as a, max(val) as b from "+twTable+ + " interval(updated_at, 5, second)) x") + + require.Len(t, node.AggList, 1) + isBoundary := node.AggList[0].GetCol() != nil + require.True(t, isBoundary, "expected the boundary to be kept as the carrier") + layout := plan2.BuildTimeWindowLayout(node) + require.Equal(t, int32(1), layout.ColCnt) + }) + + t.Run("everything discarded without a boundary keeps an aggregate carrier", func(t *testing.T) { + node := timeWindowNode(t, + "select 1 from (select max(val) as b from "+twTable+ + " interval(updated_at, 5, second)) x") + + require.Len(t, node.AggList, 1) + layout := plan2.BuildTimeWindowLayout(node) + require.Equal(t, int32(1), layout.ColCnt) + }) + + t.Run("pruning a window aggregate keeps its side effect running below", func(t *testing.T) { + // The binder rewrites a time-window aggregate's argument to a plain + // reference to the child AGG's result, so sleep(0) lives in the AGG + // node. Pruning max(...) here is safe only because AGG retains a + // side-effecting aggregate regardless of who reads it. + sql := "select a from (select _wstart as a, max(sleep(0)) as b from " + twTable + + " interval(updated_at, 5, second)) x" + + tw := timeWindowNode(t, sql) + require.Len(t, tw.AggList, 1, "the window's max(col) reference itself is inert and prunable") + requireSlotsWithinLayout(t, tw) + + logicPlan, err := buildOneStmt(plan2.NewMockOptimizer(false), t, sql) + require.NoError(t, err) + aggFound := false + for _, node := range reachablePlanNodes(logicPlan.GetQuery()) { + if node.NodeType != plan.Node_AGG { + continue + } + aggFound = true + require.Len(t, node.AggList, 1, "the side-effecting aggregate must survive") + } + require.True(t, aggFound, "the sleep(0) call must still be evaluated somewhere") + }) + + t.Run("fill columns are pruned in lockstep with the window", func(t *testing.T) { + // constructFill derives ColLen from this AggList and the operator fills + // Vecs[0..ColLen) of the window's projected batch. If the window drops + // max(val) but FILL still claims two columns, it runs off the batch. + sql := "select c from (select _wstart as a, _wend as d, max(val) as b, min(val) as c from " + + twTable + " interval(updated_at, 5, second) fill(prev)) x" + + tw := timeWindowNode(t, sql) + fill := fillNode(t, sql) + + require.Len(t, tw.AggList, 1) + require.Len(t, fill.AggList, 1) + require.Equal(t, "min", fill.AggList[0].GetF().Func.ObjName) + + // FILL's ColLen must not exceed the aggregate prefix the window emits. + layout := plan2.BuildTimeWindowLayout(tw) + require.LessOrEqual(t, len(fill.AggList), len(layout.AggIdx)) + requireSlotsWithinLayout(t, tw) + }) + + t.Run("fill(linear) values are pruned with their columns", func(t *testing.T) { + // fill(linear) builds FillVal out of references to the window's + // aggregates. Counting those references as consumers would keep every + // aggregate alive and defeat pruning entirely. + sql := "select c from (select _wstart as a, max(val) as b, min(val) as c from " + + twTable + " interval(updated_at, 5, second) fill(linear)) x" + + tw := timeWindowNode(t, sql) + fill := fillNode(t, sql) + + require.Len(t, tw.AggList, 1) + require.Len(t, fill.AggList, 1) + require.Len(t, fill.FillVal, 1) + }) +} + +func TestTimeWindowPartitionShape(t *testing.T) { + t.Run("group by keys become partition keys ordered ahead of the timestamp", func(t *testing.T) { + node := timeWindowNode(t, + "select _wstart, id, max(val) from "+twTable+" group by id interval(updated_at, 5, second)") + + require.Len(t, node.TimeWindowPartitionBy, 1) + // The operator finds boundaries by watching consecutive rows, so the + // partition key has to sort ahead of the window's timestamp. + require.Len(t, node.OrderBy, 2) + requireSlotsWithinLayout(t, node) + + // The partition key is carried in the tail slots, leaving the + // aggregates a prefix. + layout := plan2.BuildTimeWindowLayout(node) + require.Equal(t, []int32{2}, layout.PartitionSlot) + require.Equal(t, int32(3), layout.ColCnt) + }) + + t.Run("partition keys survive even when the outer query discards them", func(t *testing.T) { + // Dropping a partition key would merge groups that must stay apart, so + // it is retained for grouping even with its output unused. + node := timeWindowNode(t, + "select b from (select _wstart as a, id, max(val) as b from "+twTable+ + " group by id interval(updated_at, 5, second)) x") + + require.Len(t, node.TimeWindowPartitionBy, 1) + requireSlotsWithinLayout(t, node) + }) + + t.Run("no group by means no partitioning", func(t *testing.T) { + node := timeWindowNode(t, + "select _wstart, max(val) from "+twTable+" interval(updated_at, 5, second)") + + require.Empty(t, node.TimeWindowPartitionBy) + require.Len(t, node.OrderBy, 1) + requireSlotsWithinLayout(t, node) + }) +} diff --git a/pkg/sql/plan/opt_misc.go b/pkg/sql/plan/opt_misc.go index 7665a52022a31..ad018e74770e2 100644 --- a/pkg/sql/plan/opt_misc.go +++ b/pkg/sql/plan/opt_misc.go @@ -272,6 +272,7 @@ func replaceColumnsForNode(node *plan.Node, projMap map[[2]int32]*plan.Expr) { replaceColumnsForExprList(node.GroupBy, projMap) replaceColumnsForExprList(node.AggList, projMap) replaceColumnsForExprList(node.WinSpecList, projMap) + replaceColumnsForExprList(node.TimeWindowPartitionBy, projMap) for i := range node.OrderBy { node.OrderBy[i].Expr = replaceColumnsForExpr(node.OrderBy[i].Expr, projMap) diff --git a/pkg/sql/plan/query_builder.go b/pkg/sql/plan/query_builder.go index ed80af0d71419..32d878768796a 100644 --- a/pkg/sql/plan/query_builder.go +++ b/pkg/sql/plan/query_builder.go @@ -1500,126 +1500,171 @@ func (builder *QueryBuilder) remapAllColRefs(nodeID int32, step int32, colRefCnt } case plan.Node_TIME_WINDOW: - for _, expr := range node.AggList { + timeTag := node.BindingTags[0] + + // Decide what survives before touching colRefCnt: a `_wstart`/`_wend` + // entry of AggList is a column reference to its own {timeTag, k}, so + // counting the AggList as a consumer would make every boundary look + // referenced by itself. + retained := make([]bool, len(node.AggList)) + anyRetained := false + for k, expr := range node.AggList { + if colRefCnt[[2]int32{timeTag, int32(k)}] > 0 || !exprCanRemoveProject(expr) { + retained[k] = true + anyRetained = true + } + } + // calRes sizes the output off Vecs[0], so the operator cannot emit a + // zero-column batch, and the window count is the row count an outer + // constant projection still depends on. Keep one carrier: a boundary + // first, since it runs no aggregate. + if !anyRetained && len(node.AggList) > 0 { + carrier := 0 + for k, expr := range node.AggList { + if isBoundary, _ := isTimeWindowBoundary(expr); isBoundary { + carrier = k + break + } + } + retained[carrier] = true + } + + // The partition keys decide which rows share a window, so they are + // never pruned: dropping one would merge groups that must stay apart. + // Record where each one came from before remapping rewrites it to a + // child-local reference. + partitionSrc := make([]int32, len(node.TimeWindowPartitionBy)) + for p, expr := range node.TimeWindowPartitionBy { + partitionSrc[p] = -1 + if col := expr.GetCol(); col != nil { + partitionSrc[p] = col.ColPos + } + } + + // Only retained aggregates consume child columns. Withholding the + // pruned ones here is what lets the child AGG drop their inputs too. + for k, expr := range node.AggList { + if isBoundary, _ := isTimeWindowBoundary(expr); isBoundary || !retained[k] { + continue + } + increaseRefCnt(expr, 1, colRefCnt) + } + increaseRefCnt(node.GroupBy[0], 1, colRefCnt) + for _, orderBy := range node.OrderBy { + increaseRefCnt(orderBy.Expr, 1, colRefCnt) + } + for _, expr := range node.TimeWindowPartitionBy { increaseRefCnt(expr, 1, colRefCnt) } - increaseRefCnt(node.OrderBy[0].Expr, 1, colRefCnt) childRemapping, err := builder.remapAllColRefs(node.Children[0], step, colRefCnt, colRefBool, sinkColRef) if err != nil { return nil, err } - timeTag := node.BindingTags[0] - groupTag := node.BindingTags[1] - - // order by - idx := 0 - increaseRefCnt(node.OrderBy[0].Expr, -1, colRefCnt) - remapInfo.tip = "OrderBy[0].Expr" + increaseRefCnt(node.GroupBy[0], -1, colRefCnt) + remapInfo.tip = "GroupBy[0]" remapInfo.srcExprIdx = 0 - err = builder.remapColRefForExpr(node.OrderBy[0].Expr, childRemapping.globalToLocal, &remapInfo) + err = builder.remapColRefForExpr(node.GroupBy[0], childRemapping.globalToLocal, &remapInfo) if err != nil { return nil, err } - globalRef := [2]int32{groupTag, int32(0)} - if colRefCnt[globalRef] != 0 { - remapping.addColRef(globalRef) - node.ProjectList = append(node.ProjectList, &plan.Expr{ - Typ: node.OrderBy[0].Expr.Typ, - Expr: &plan.Expr_Col{ - Col: &ColRef{ - RelPos: -1, - ColPos: int32(idx), - Name: builder.nameByColRef[globalRef], - }, - }, - }) - idx++ + remapInfo.tip = "OrderBy" + for idx, orderBy := range node.OrderBy { + increaseRefCnt(orderBy.Expr, -1, colRefCnt) + remapInfo.srcExprIdx = idx + err = builder.remapColRefForExpr(orderBy.Expr, childRemapping.globalToLocal, &remapInfo) + if err != nil { + return nil, err + } } - var wstart, wend *plan.Expr - var i, j int - remapInfo.tip = "AggList" - for k, expr := range node.AggList { - if e, ok := expr.Expr.(*plan.Expr_Col); ok { - if e.Col.Name == TimeWindowStart { - wstart = expr - i = k - } - if e.Col.Name == TimeWindowEnd { - wend = expr - j = k - } - continue - } + remapInfo.tip = "TimeWindowPartitionBy" + for idx, expr := range node.TimeWindowPartitionBy { increaseRefCnt(expr, -1, colRefCnt) - remapInfo.srcExprIdx = k + remapInfo.srcExprIdx = idx err = builder.remapColRefForExpr(expr, childRemapping.globalToLocal, &remapInfo) if err != nil { return nil, err } + } - globalRef := [2]int32{timeTag, int32(k)} - if colRefCnt[globalRef] == 0 { + remapInfo.tip = "AggList" + newAggList := make([]*plan.Expr, 0, len(node.AggList)) + // The outer query still addresses these values by their original + // AggList position, so keep the compacted index's origin. + newToOld := make([]int32, 0, len(node.AggList)) + for k, expr := range node.AggList { + if !retained[k] { continue } - - remapping.addColRef(globalRef) - - node.ProjectList = append(node.ProjectList, &plan.Expr{ - Typ: expr.Typ, - Expr: &plan.Expr_Col{ - Col: &ColRef{ - RelPos: -1, - ColPos: int32(idx), - Name: builder.nameByColRef[globalRef], - }, - }, - }) - idx++ - } - - if wstart != nil { - increaseRefCnt(wstart, -1, colRefCnt) - - globalRef := [2]int32{timeTag, int32(i)} - if colRefCnt[globalRef] == 0 { - break + if isBoundary, _ := isTimeWindowBoundary(expr); !isBoundary { + increaseRefCnt(expr, -1, colRefCnt) + remapInfo.srcExprIdx = k + err = builder.remapColRefForExpr(expr, childRemapping.globalToLocal, &remapInfo) + if err != nil { + return nil, err + } } + newAggList = append(newAggList, expr) + newToOld = append(newToOld, int32(k)) + } + node.AggList = newAggList - remapping.addColRef(globalRef) + // Address slots through the same layout the operator is built from, + // and emit in slot order so the aggregates stay a prefix -- FILL reads + // this projection positionally. + layout := BuildTimeWindowLayout(node) + for slot := int32(0); slot < layout.ColCnt; slot++ { + for k, expr := range node.AggList { + if layout.Slot[k] != slot { + continue + } + globalRef := [2]int32{timeTag, newToOld[k]} + if colRefCnt[globalRef] == 0 { + continue + } + remapping.addColRef(globalRef) - node.ProjectList = append(node.ProjectList, &plan.Expr{ - Typ: node.Timestamp.Typ, - Expr: &plan.Expr_Col{ - Col: &ColRef{ - RelPos: -1, - ColPos: int32(idx), - Name: builder.nameByColRef[globalRef], + typ := expr.Typ + if isBoundary, _ := isTimeWindowBoundary(expr); isBoundary { + typ = node.Timestamp.Typ + } + node.ProjectList = append(node.ProjectList, &plan.Expr{ + Typ: typ, + Expr: &plan.Expr_Col{ + Col: &ColRef{ + RelPos: -1, + ColPos: slot, + Name: builder.nameByColRef[globalRef], + }, }, - }, - }) - idx++ + }) + } } - if wend != nil { - increaseRefCnt(wend, -1, colRefCnt) - - globalRef := [2]int32{timeTag, int32(j)} + // Partition keys are carried through so a GROUP BY column stays + // selectable next to the window's aggregates. The operator always + // evaluates them to find its boundaries, so only their output is + // optional. + groupTag := node.BindingTags[1] + for p, slot := range layout.PartitionSlot { + if partitionSrc[p] < 0 { + continue + } + globalRef := [2]int32{groupTag, partitionSrc[p]} if colRefCnt[globalRef] == 0 { - break + continue } - remapping.addColRef(globalRef) node.ProjectList = append(node.ProjectList, &plan.Expr{ - Typ: node.Timestamp.Typ, + Typ: node.TimeWindowPartitionBy[p].Typ, Expr: &plan.Expr_Col{ Col: &ColRef{ RelPos: -1, - ColPos: int32(idx), + ColPos: slot, Name: builder.nameByColRef[globalRef], }, }, @@ -1786,6 +1831,76 @@ func (builder *QueryBuilder) remapAllColRefs(nodeID int32, step int32, colRefCnt case plan.Node_FILL: + // The fill operator addresses its input positionally: it fills + // Vecs[0..ColLen), which must line up with the aggregates the child + // TIME_WINDOW projects. That child prunes the aggregates the outer + // query does not reference, so drop the matching fill columns here or + // the operator walks off the end of the batch. + // + // The decision has to be made before the FillVal refcounts below, + // because fill(linear) builds its FillVal out of references to those + // very aggregates and would otherwise keep every one of them alive. + var sideEffectRefs [][2]int32 + // fill(prev/next/linear) must not carry values across a partition + // boundary, so the operator needs the partition keys in its input. + // Force the child window to project them -- the count is dropped after + // the child is remapped, exactly like the side-effect refs below -- and + // resolve their positions once the child's projection exists. + var fillPartitionRefs [][2]int32 + child := builder.qry.Nodes[node.Children[0]] + fillNeedsPartitions := node.FillType == plan.Node_PREV || + node.FillType == plan.Node_NEXT || node.FillType == plan.Node_LINEAR + if child.NodeType == plan.Node_TIME_WINDOW && len(child.TimeWindowPartitionBy) > 0 && fillNeedsPartitions { + childGroupTag := child.BindingTags[1] + for _, expr := range child.TimeWindowPartitionBy { + if col := expr.GetCol(); col != nil { + ref := [2]int32{childGroupTag, col.ColPos} + colRefCnt[ref]++ + fillPartitionRefs = append(fillPartitionRefs, ref) + } + } + } + if child.NodeType == plan.Node_TIME_WINDOW && len(node.AggList) > 0 { + timeTag := child.BindingTags[0] + childLayout := BuildTimeWindowLayout(child) + + newAggList := node.AggList[:0] + newFillVal := node.FillVal[:0] + for i := range node.AggList { + if i >= len(childLayout.AggIdx) { + break + } + // node.AggList[i] is the i'th real aggregate of the child's + // AggList: both were built from the same ctx.times, skipping + // the `_wstart`/`_wend` carriers. + if colRefCnt[[2]int32{timeTag, childLayout.AggIdx[i]}] == 0 { + // A side-effecting fill value (e.g. fill(value, sleep(1))) + // is observable even when the filled column is discarded. + // Keep the column and register this node as its consumer, + // so the child window retains the matching aggregate slot. + // The count is released after the child is remapped: no + // one above reads the column, so FILL must not project it. + if i < len(node.FillVal) && !exprCanRemoveProject(node.FillVal[i]) { + ref := [2]int32{timeTag, childLayout.AggIdx[i]} + colRefCnt[ref]++ + sideEffectRefs = append(sideEffectRefs, ref) + } else { + continue + } + } + newAggList = append(newAggList, node.AggList[i]) + if i < len(node.FillVal) { + newFillVal = append(newFillVal, node.FillVal[i]) + } + } + clear(node.AggList[len(newAggList):]) + node.AggList = newAggList + if node.FillVal != nil { + clear(node.FillVal[len(newFillVal):]) + node.FillVal = newFillVal + } + } + for _, expr := range node.FillVal { increaseRefCnt(expr, 1, colRefCnt) } @@ -1794,6 +1909,19 @@ func (builder *QueryBuilder) remapAllColRefs(nodeID int32, step int32, colRefCnt if err != nil { return nil, err } + for _, ref := range sideEffectRefs { + colRefCnt[ref]-- + } + node.TimeWindowPartitionColPos = node.TimeWindowPartitionColPos[:0] + for _, ref := range fillPartitionRefs { + colRefCnt[ref]-- + localRef, ok := childRemapping.globalToLocal[ref] + if !ok { + return nil, moerr.NewInternalError(builder.GetContext(), + "fill: partition key missing from the time window's projection") + } + node.TimeWindowPartitionColPos = append(node.TimeWindowPartitionColPos, localRef[1]) + } for _, expr := range node.FillVal { increaseRefCnt(expr, -1, colRefCnt) @@ -4907,8 +5035,11 @@ func (builder *QueryBuilder) bindTimeWindow( } } + // Copy rather than alias the group expression: remapping walks OrderBy and + // GroupBy separately, and rewriting one shared pointer twice would resolve + // an already-local column reference a second time. boundTimeWindowOrderBy = &plan.OrderBySpec{ - Expr: timeWindowGroup, + Expr: DeepCopyExpr(timeWindowGroup), Flag: plan.OrderBySpec_INTERNAL | plan.OrderBySpec_ASC | plan.OrderBySpec_NULLS_FIRST, } @@ -5325,17 +5456,49 @@ func (builder *QueryBuilder) appendTimeWindowNode( return } + // A GROUP BY alongside the window puts extra keys in ctx.groups next to the + // window's own truncated timestamp. Those keys partition the window: each + // group gets its own window sequence rather than one stream shared by all. + // Ordering by them first is what makes each partition contiguous for the + // operator. + var partitionBy []*plan.Expr + orderBy := []*plan.OrderBySpec{boundTimeWindowOrderBy} + if tsCol := boundTimeWindowGroupBy.GetCol(); tsCol != nil { + partitionOrderBy := make([]*plan.OrderBySpec, 0, len(ctx.groups)) + for i := range ctx.groups { + if int32(i) == tsCol.ColPos { + continue + } + partitionCol := &plan.Expr{ + Typ: ctx.groups[i].Typ, + Expr: &plan.Expr_Col{ + Col: &plan.ColRef{ + RelPos: ctx.groupTag, + ColPos: int32(i), + }, + }, + } + partitionBy = append(partitionBy, partitionCol) + partitionOrderBy = append(partitionOrderBy, &plan.OrderBySpec{ + Expr: DeepCopyExpr(partitionCol), + Flag: plan.OrderBySpec_INTERNAL | plan.OrderBySpec_ASC | plan.OrderBySpec_NULLS_FIRST, + }) + } + orderBy = append(partitionOrderBy, orderBy...) + } + nodeID = builder.appendNode(&plan.Node{ - NodeType: plan.Node_TIME_WINDOW, - Children: []int32{nodeID}, - AggList: ctx.times, - BindingTags: []int32{ctx.timeTag, ctx.groupTag}, - OrderBy: []*plan.OrderBySpec{boundTimeWindowOrderBy}, - Interval: interval, - Sliding: sliding, - GroupBy: []*plan.Expr{boundTimeWindowGroupBy}, - Timestamp: ts, - WEnd: wEnd, + NodeType: plan.Node_TIME_WINDOW, + Children: []int32{nodeID}, + AggList: ctx.times, + BindingTags: []int32{ctx.timeTag, ctx.groupTag}, + OrderBy: orderBy, + Interval: interval, + Sliding: sliding, + GroupBy: []*plan.Expr{boundTimeWindowGroupBy}, + TimeWindowPartitionBy: partitionBy, + Timestamp: ts, + WEnd: wEnd, }, ctx) for name, id := range ctx.timeByAst { diff --git a/pkg/sql/plan/time_window_layout.go b/pkg/sql/plan/time_window_layout.go new file mode 100644 index 0000000000000..c2e7763a99832 --- /dev/null +++ b/pkg/sql/plan/time_window_layout.go @@ -0,0 +1,130 @@ +// Copyright 2021 - 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "github.com/matrixorigin/matrixone/pkg/pb/plan" +) + +// TimeWindowSlotNone marks an AggList entry that occupies no output slot. +const TimeWindowSlotNone = int32(-1) + +// TimeWindowLayout describes the output batch a TIME_WINDOW operator produces. +// +// The operator emits, in this order: +// +// [ aggregate results (AggList order) ..., _wstart?, _wend?, partition keys... ] +// +// Every `_wstart` entry in AggList maps to the single WStartSlot, and likewise +// for `_wend`, because the operator only carries one boolean per boundary +// rather than one column per reference. +// +// Partition keys go last so the aggregates stay a prefix: the fill operator +// addresses its input positionally as Vecs[0..ColLen). +// +// This is the single source of truth for that layout: the planner uses it to +// address slots from a TIME_WINDOW ProjectList, and the compiler uses it to +// build the operator that fills them. Deriving it in two places is what let +// the two drift apart and read each other's columns. +type TimeWindowLayout struct { + // Slot maps an AggList index to its output position, or TimeWindowSlotNone + // for entries that occupy none (every `_wstart`/`_wend` entry shares one + // slot, so only the boundary slots below are authoritative for them). + Slot []int32 + // AggIdx lists the AggList indices holding real aggregates, in slot order. + AggIdx []int32 + // WStartSlot / WEndSlot are the boundary positions, or TimeWindowSlotNone + // when the boundary is not produced. + WStartSlot int32 + WEndSlot int32 + // PartitionSlot maps a TimeWindowPartitionBy index to its output position. + PartitionSlot []int32 + // ColCnt is the number of vectors in the output batch. + ColCnt int32 +} + +// isTimeWindowBoundary reports whether an AggList entry is a `_wstart`/`_wend` +// carrier rather than an aggregate. It mirrors constructTimeWindow: any column +// reference is a carrier, and only the two known names produce a boundary. +func isTimeWindowBoundary(expr *plan.Expr) (isBoundary bool, name string) { + e, ok := expr.Expr.(*plan.Expr_Col) + if !ok { + return false, "" + } + return true, e.Col.Name +} + +// BuildTimeWindowLayout derives the output layout of the TIME_WINDOW operator +// for the given node. +func BuildTimeWindowLayout(node *plan.Node) TimeWindowLayout { + layout := buildTimeWindowAggLayout(node.AggList) + for range node.TimeWindowPartitionBy { + layout.PartitionSlot = append(layout.PartitionSlot, layout.ColCnt) + layout.ColCnt++ + } + return layout +} + +// buildTimeWindowAggLayout lays out the aggregate and boundary slots, which +// occupy the front of the batch regardless of partitioning. +func buildTimeWindowAggLayout(aggList []*plan.Expr) TimeWindowLayout { + layout := TimeWindowLayout{ + Slot: make([]int32, len(aggList)), + WStartSlot: TimeWindowSlotNone, + WEndSlot: TimeWindowSlotNone, + } + + hasWStart, hasWEnd := false, false + for i, expr := range aggList { + layout.Slot[i] = TimeWindowSlotNone + isBoundary, name := isTimeWindowBoundary(expr) + if !isBoundary { + layout.Slot[i] = int32(len(layout.AggIdx)) + layout.AggIdx = append(layout.AggIdx, int32(i)) + continue + } + switch name { + case TimeWindowStart: + hasWStart = true + case TimeWindowEnd: + hasWEnd = true + } + } + + layout.ColCnt = int32(len(layout.AggIdx)) + if hasWStart { + layout.WStartSlot = layout.ColCnt + layout.ColCnt++ + } + if hasWEnd { + layout.WEndSlot = layout.ColCnt + layout.ColCnt++ + } + + for i, expr := range aggList { + isBoundary, name := isTimeWindowBoundary(expr) + if !isBoundary { + continue + } + switch name { + case TimeWindowStart: + layout.Slot[i] = layout.WStartSlot + case TimeWindowEnd: + layout.Slot[i] = layout.WEndSlot + } + } + + return layout +} diff --git a/pkg/sql/plan/time_window_layout_test.go b/pkg/sql/plan/time_window_layout_test.go new file mode 100644 index 0000000000000..23c42e43c658a --- /dev/null +++ b/pkg/sql/plan/time_window_layout_test.go @@ -0,0 +1,176 @@ +// Copyright 2021 - 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/stretchr/testify/require" +) + +func twBoundary(name string) *plan.Expr { + return &plan.Expr{ + Expr: &plan.Expr_Col{Col: &plan.ColRef{Name: name}}, + } +} + +func twAgg(name string) *plan.Expr { + return &plan.Expr{ + Expr: &plan.Expr_F{ + F: &plan.Function{Func: &plan.ObjectRef{ObjName: name}}, + }, + } +} + +func TestBuildTimeWindowLayout(t *testing.T) { + cases := []struct { + name string + aggList []*plan.Expr + partitionBy int + wantSlot []int32 + wantAggIdx []int32 + wantWStart int32 + wantWEnd int32 + wantPartSlt []int32 + wantColCnt int32 + }{ + { + name: "aggregates only", + aggList: []*plan.Expr{twAgg("max"), twAgg("min")}, + wantSlot: []int32{0, 1}, + wantAggIdx: []int32{0, 1}, + wantWStart: TimeWindowSlotNone, + wantWEnd: TimeWindowSlotNone, + wantColCnt: 2, + }, + { + // Boundaries trail the aggregates no matter where they sit in + // AggList; the binder puts _wstart first for `select _wstart, max(v)`. + name: "boundaries follow aggregates regardless of AggList order", + aggList: []*plan.Expr{twBoundary(TimeWindowStart), twBoundary(TimeWindowEnd), twAgg("max")}, + wantSlot: []int32{1, 2, 0}, + wantAggIdx: []int32{2}, + wantWStart: 1, + wantWEnd: 2, + wantColCnt: 3, + }, + { + name: "wend without wstart takes the first boundary slot", + aggList: []*plan.Expr{twBoundary(TimeWindowEnd), twAgg("max")}, + wantSlot: []int32{1, 0}, + wantAggIdx: []int32{0 + 1}, + wantWStart: TimeWindowSlotNone, + wantWEnd: 1, + wantColCnt: 2, + }, + { + // The operator carries one flag per boundary, not one column per + // reference, so repeats collapse onto the same slot. + name: "repeated wstart references share one slot", + aggList: []*plan.Expr{twBoundary(TimeWindowStart), twAgg("max"), twBoundary(TimeWindowStart)}, + wantSlot: []int32{1, 0, 1}, + wantAggIdx: []int32{1}, + wantWStart: 1, + wantWEnd: TimeWindowSlotNone, + wantColCnt: 2, + }, + { + name: "boundary only", + aggList: []*plan.Expr{twBoundary(TimeWindowStart)}, + wantSlot: []int32{0}, + wantAggIdx: nil, + wantWStart: 0, + wantWEnd: TimeWindowSlotNone, + wantColCnt: 1, + }, + { + name: "partition keys come last", + aggList: []*plan.Expr{twBoundary(TimeWindowStart), twAgg("max")}, + partitionBy: 2, + wantSlot: []int32{1, 0}, + wantAggIdx: []int32{1}, + wantWStart: 1, + wantWEnd: TimeWindowSlotNone, + wantPartSlt: []int32{2, 3}, + wantColCnt: 4, + }, + { + name: "empty", + aggList: nil, + wantSlot: []int32{}, + wantAggIdx: nil, + wantWStart: TimeWindowSlotNone, + wantWEnd: TimeWindowSlotNone, + wantColCnt: 0, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + node := &plan.Node{AggList: c.aggList} + for i := 0; i < c.partitionBy; i++ { + node.TimeWindowPartitionBy = append(node.TimeWindowPartitionBy, twAgg("col")) + } + + layout := BuildTimeWindowLayout(node) + require.Equal(t, c.wantSlot, layout.Slot) + require.Equal(t, c.wantAggIdx, layout.AggIdx) + require.Equal(t, c.wantWStart, layout.WStartSlot) + require.Equal(t, c.wantWEnd, layout.WEndSlot) + require.Equal(t, c.wantPartSlt, layout.PartitionSlot) + require.Equal(t, c.wantColCnt, layout.ColCnt) + }) + } +} + +// The compiler builds the operator's columns from AggIdx and the boundary +// flags while the planner addresses them through Slot. If those two views ever +// disagree the projection reads someone else's column, which is exactly the +// drift this type exists to prevent. +func TestBuildTimeWindowLayoutSlotsAreConsistent(t *testing.T) { + node := &plan.Node{ + AggList: []*plan.Expr{ + twBoundary(TimeWindowEnd), + twAgg("max"), + twBoundary(TimeWindowStart), + twAgg("min"), + }, + TimeWindowPartitionBy: []*plan.Expr{twAgg("col")}, + } + layout := BuildTimeWindowLayout(node) + + // Aggregates occupy a dense prefix, in AggList order. + for want, aggIdx := range layout.AggIdx { + require.Equal(t, int32(want), layout.Slot[aggIdx]) + } + require.Equal(t, []int32{1, 3}, layout.AggIdx) + + // Boundaries sit immediately after, and every reference agrees with the + // authoritative boundary slot. + require.Equal(t, int32(2), layout.WStartSlot) + require.Equal(t, int32(3), layout.WEndSlot) + require.Equal(t, layout.WStartSlot, layout.Slot[2]) + require.Equal(t, layout.WEndSlot, layout.Slot[0]) + + // Partition keys take the tail, leaving the aggregates a prefix for FILL. + require.Equal(t, []int32{4}, layout.PartitionSlot) + require.Equal(t, int32(5), layout.ColCnt) + + for _, slot := range layout.Slot { + require.Less(t, slot, layout.ColCnt) + require.GreaterOrEqual(t, slot, int32(0)) + } +} diff --git a/pkg/sql/plan/visit_plan.go b/pkg/sql/plan/visit_plan.go index 53ae2239c810b..d8be2ecda1149 100644 --- a/pkg/sql/plan/visit_plan.go +++ b/pkg/sql/plan/visit_plan.go @@ -132,6 +132,13 @@ func (vq *VisitPlan) exploreNode(ctx context.Context, rule VisitPlanRule, node * } } + for i := range node.TimeWindowPartitionBy { + node.TimeWindowPartitionBy[i], err = rule.ApplyExpr(node.TimeWindowPartitionBy[i]) + if err != nil { + return err + } + } + for i := range node.BlockFilterList { node.BlockFilterList[i], err = rule.ApplyExpr(node.BlockFilterList[i]) if err != nil { diff --git a/proto/plan.proto b/proto/plan.proto index 112d813a3c76c..7c667a4df255e 100644 --- a/proto/plan.proto +++ b/proto/plan.proto @@ -941,6 +941,17 @@ message Node { int64 spill_mem = 70; repeated string origin_views = 71; string direct_view = 72; + + // TIME_WINDOW only: the GROUP BY columns other than the window's own + // timestamp key. The operator restarts its window state whenever these + // change, so each group gets its own windows instead of one global stream. + repeated Expr time_window_partition_by = 73; + + // FILL only: positions of the partition keys inside this node's input batch + // (the child TIME_WINDOW's projection). fill(prev/next/linear) must not + // carry values across a partition boundary, so the operator compares these + // columns to find where one group ends and the next begins. + repeated int32 time_window_partition_col_pos = 74; } message SnapshotExtraInfo { diff --git a/test/distributed/cases/optimizer/column_pruning.result b/test/distributed/cases/optimizer/column_pruning.result index c21d43bbac63b..1f884f516f217 100644 --- a/test/distributed/cases/optimizer/column_pruning.result +++ b/test/distributed/cases/optimizer/column_pruning.result @@ -126,4 +126,126 @@ insert into tw values ('2024-01-01 00:00:00', 1), ('2024-01-01 12:00:00', 2); select _wend, max(v) from tw interval(ts, 1, day); _wend max(v) 2024-01-02 00:00:00 2 +create table tws(ts timestamp(3) primary key, v int); +insert into tws values +('2023-08-01 00:00:00', 10), ('2023-08-01 00:00:01', 20), +('2023-08-01 00:00:06', 30), ('2023-08-01 00:00:07', 40); +select _wstart, _wend, max(v), min(v) from tws interval(ts, 5, second); +➤ _wstart[93,64,0] ¦ _wend[93,64,0] ¦ max(v)[4,32,0] ¦ min(v)[4,32,0] 𝄀 +2023-08-01 00:00:00 ¦ 2023-08-01 00:00:05 ¦ 20 ¦ 10 𝄀 +2023-08-01 00:00:05 ¦ 2023-08-01 00:00:10 ¦ 40 ¦ 30 +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second)) x; +➤ c[4,32,0] 𝄀 +10 𝄀 +30 +select a from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second)) x; +➤ a[93,64,0] 𝄀 +2023-08-01 00:00:00 𝄀 +2023-08-01 00:00:05 +select d from (select _wstart as a, _wend as d, max(v) as b from tws interval(ts, 5, second)) x; +➤ d[93,64,0] 𝄀 +2023-08-01 00:00:05 𝄀 +2023-08-01 00:00:10 +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 10, second) sliding(5, second)) x; +➤ c[4,32,0] 𝄀 +10 𝄀 +30 +select a from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 10, second) sliding(5, second)) x; +➤ a[93,64,0] 𝄀 +2023-08-01 00:00:00 𝄀 +2023-08-01 00:00:05 +select c from (select _wstart as a, _wend as d, max(v) as b, min(v) as c from tws interval(ts, 5, second) fill(prev)) x; +➤ c[4,32,0] 𝄀 +10 𝄀 +30 +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second) fill(linear)) x; +➤ c[4,32,0] 𝄀 +10 𝄀 +30 +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second) fill(value, 99)) x; +➤ c[4,32,0] 𝄀 +10 𝄀 +30 +select 1 from (select _wstart as a, max(v) as b from tws interval(ts, 5, second)) x; +➤ 1[-5,64,0] 𝄀 +1 𝄀 +1 +select count(*) from (select _wstart as a, max(v) as b from tws interval(ts, 5, second)) x; +➤ count(*)[-5,64,0] 𝄀 +2 +select 1 from (select max(v) as b from tws interval(ts, 5, second)) x; +➤ 1[-5,64,0] 𝄀 +1 𝄀 +1 +create table tw_empty(ts timestamp(3) primary key, v int); +select _wstart, max(v) from tw_empty interval(ts, 5, second); +➤ _wstart[93,64,0] ¦ max(v)[4,32,0] +select 1 from (select _wstart as a, max(v) as b from tw_empty interval(ts, 5, second)) x; +➤ 1[-5,64,0] +create table twp(ts timestamp(3), p int, v int); +insert into twp values +('2023-08-01 00:00:00', 1, 10), ('2023-08-01 00:00:01', 1, 20), +('2023-08-01 00:00:06', 1, 30), ('2023-08-01 00:00:00', 2, 100), +('2023-08-01 00:00:07', 2, 400); +select _wstart, p, max(v) from twp group by p interval(ts, 5, second) order by p, _wstart; +➤ _wstart[93,64,0] ¦ p[4,32,0] ¦ max(v)[4,32,0] 𝄀 +2023-08-01 00:00:00 ¦ 1 ¦ 20 𝄀 +2023-08-01 00:00:05 ¦ 1 ¦ 30 𝄀 +2023-08-01 00:00:00 ¦ 2 ¦ 100 𝄀 +2023-08-01 00:00:05 ¦ 2 ¦ 400 +select _wstart, _wend, p, max(v), min(v) from twp group by p interval(ts, 5, second) order by p, _wstart; +➤ _wstart[93,64,0] ¦ _wend[93,64,0] ¦ p[4,32,0] ¦ max(v)[4,32,0] ¦ min(v)[4,32,0] 𝄀 +2023-08-01 00:00:00 ¦ 2023-08-01 00:00:05 ¦ 1 ¦ 20 ¦ 10 𝄀 +2023-08-01 00:00:05 ¦ 2023-08-01 00:00:10 ¦ 1 ¦ 30 ¦ 30 𝄀 +2023-08-01 00:00:00 ¦ 2023-08-01 00:00:05 ¦ 2 ¦ 100 ¦ 100 𝄀 +2023-08-01 00:00:05 ¦ 2023-08-01 00:00:10 ¦ 2 ¦ 400 ¦ 400 +select p, _wstart, max(v), min(v) from twp group by p interval(ts, 10, second) sliding(5, second) order by p, _wstart; +➤ p[4,32,0] ¦ _wstart[93,64,0] ¦ max(v)[4,32,0] ¦ min(v)[4,32,0] 𝄀 +1 ¦ 2023-08-01 00:00:00 ¦ 30 ¦ 10 𝄀 +1 ¦ 2023-08-01 00:00:05 ¦ 30 ¦ 30 𝄀 +2 ¦ 2023-08-01 00:00:00 ¦ 400 ¦ 100 𝄀 +2 ¦ 2023-08-01 00:00:05 ¦ 400 ¦ 400 +select b from (select _wstart as a, p, max(v) as b from twp group by p interval(ts, 5, second)) x order by b; +➤ b[4,32,0] 𝄀 +20 𝄀 +30 𝄀 +100 𝄀 +400 +select count(*) from (select _wstart as a, p, max(v) as b from twp group by p interval(ts, 5, second)) x; +➤ count(*)[-5,64,0] 𝄀 +4 +create table twf(ts datetime, p int, v int); +insert into twf values +('2026-01-01 00:00:00', 1, 10), ('2026-01-01 00:00:07', 1, null), +('2026-01-01 00:00:00', 2, null), ('2026-01-01 00:00:07', 2, 40); +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) order by p, _wstart; +➤ p[4,32,0] ¦ _wstart[93,64,0] ¦ max(v)[4,32,0] 𝄀 +1 ¦ 2026-01-01 00:00:00 ¦ 10 𝄀 +1 ¦ 2026-01-01 00:00:05 ¦ null 𝄀 +2 ¦ 2026-01-01 00:00:00 ¦ null 𝄀 +2 ¦ 2026-01-01 00:00:05 ¦ 40 +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) fill(prev) order by p, _wstart; +➤ p[4,32,0] ¦ _wstart[93,64,0] ¦ max(v)[4,32,0] 𝄀 +1 ¦ 2026-01-01 00:00:00 ¦ 10 𝄀 +1 ¦ 2026-01-01 00:00:05 ¦ 10 𝄀 +2 ¦ 2026-01-01 00:00:00 ¦ null 𝄀 +2 ¦ 2026-01-01 00:00:05 ¦ 40 +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) fill(next) order by p, _wstart; +➤ p[4,32,0] ¦ _wstart[93,64,0] ¦ max(v)[4,32,0] 𝄀 +1 ¦ 2026-01-01 00:00:00 ¦ 10 𝄀 +1 ¦ 2026-01-01 00:00:05 ¦ null 𝄀 +2 ¦ 2026-01-01 00:00:00 ¦ 40 𝄀 +2 ¦ 2026-01-01 00:00:05 ¦ 40 +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) fill(value, 99) order by p, _wstart; +➤ p[4,32,0] ¦ _wstart[93,64,0] ¦ max(v)[4,32,0] 𝄀 +1 ¦ 2026-01-01 00:00:00 ¦ 10 𝄀 +1 ¦ 2026-01-01 00:00:05 ¦ 99 𝄀 +2 ¦ 2026-01-01 00:00:00 ¦ 99 𝄀 +2 ¦ 2026-01-01 00:00:05 ¦ 40 +select p, _wstart, max(v) from twf group by p interval(ts, 10, second) sliding(5, second) fill(prev) order by p, _wstart; +➤ p[4,32,0] ¦ _wstart[93,64,0] ¦ max(v)[4,32,0] 𝄀 +1 ¦ 2026-01-01 00:00:00 ¦ 10 𝄀 +1 ¦ 2026-01-01 00:00:05 ¦ 10 𝄀 +2 ¦ 2026-01-01 00:00:00 ¦ 40 𝄀 +2 ¦ 2026-01-01 00:00:05 ¦ 40 drop database column_pruning; diff --git a/test/distributed/cases/optimizer/column_pruning.test b/test/distributed/cases/optimizer/column_pruning.test index ac8f758e88cb6..281c3c4818d38 100644 --- a/test/distributed/cases/optimizer/column_pruning.test +++ b/test/distributed/cases/optimizer/column_pruning.test @@ -90,4 +90,69 @@ create table tw(ts timestamp, v int); insert into tw values ('2024-01-01 00:00:00', 1), ('2024-01-01 12:00:00', 2); select _wend, max(v) from tw interval(ts, 1, day); +-- A time window's outputs are addressed by physical slot. Discarding any of +-- them compacts those slots, so each surviving output must still resolve to +-- its own value rather than to the neighbour that used to occupy the position. +create table tws(ts timestamp(3) primary key, v int); +insert into tws values + ('2023-08-01 00:00:00', 10), ('2023-08-01 00:00:01', 20), + ('2023-08-01 00:00:06', 30), ('2023-08-01 00:00:07', 40); + +-- baseline: every output consumed +select _wstart, _wend, max(v), min(v) from tws interval(ts, 5, second); + +-- only the trailing aggregate survives +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second)) x; +-- only the leading boundary survives +select a from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second)) x; +-- _wstart discarded, _wend kept +select d from (select _wstart as a, _wend as d, max(v) as b from tws interval(ts, 5, second)) x; +-- same shapes with sliding windows +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 10, second) sliding(5, second)) x; +select a from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 10, second) sliding(5, second)) x; + +-- fill() reads the window's aggregates by position, so its columns have to be +-- pruned in step with them. +select c from (select _wstart as a, _wend as d, max(v) as b, min(v) as c from tws interval(ts, 5, second) fill(prev)) x; +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second) fill(linear)) x; +select c from (select _wstart as a, max(v) as b, min(v) as c from tws interval(ts, 5, second) fill(value, 99)) x; + +-- Every value is discarded, but one row per window must still be produced. +select 1 from (select _wstart as a, max(v) as b from tws interval(ts, 5, second)) x; +select count(*) from (select _wstart as a, max(v) as b from tws interval(ts, 5, second)) x; +select 1 from (select max(v) as b from tws interval(ts, 5, second)) x; + +-- Empty input produces no windows. +create table tw_empty(ts timestamp(3) primary key, v int); +select _wstart, max(v) from tw_empty interval(ts, 5, second); +select 1 from (select _wstart as a, max(v) as b from tw_empty interval(ts, 5, second)) x; + +-- GROUP BY alongside a time window partitions it: each group gets its own +-- windows rather than sharing one stream with the others. +create table twp(ts timestamp(3), p int, v int); +insert into twp values + ('2023-08-01 00:00:00', 1, 10), ('2023-08-01 00:00:01', 1, 20), + ('2023-08-01 00:00:06', 1, 30), ('2023-08-01 00:00:00', 2, 100), + ('2023-08-01 00:00:07', 2, 400); + +select _wstart, p, max(v) from twp group by p interval(ts, 5, second) order by p, _wstart; +select _wstart, _wend, p, max(v), min(v) from twp group by p interval(ts, 5, second) order by p, _wstart; +select p, _wstart, max(v), min(v) from twp group by p interval(ts, 10, second) sliding(5, second) order by p, _wstart; +-- the partition key decides row grouping, so it is kept even when unselected +select b from (select _wstart as a, p, max(v) as b from twp group by p interval(ts, 5, second)) x order by b; +select count(*) from (select _wstart as a, p, max(v) as b from twp group by p interval(ts, 5, second)) x; + +-- fill() must not carry values across partitions: each group's leading NULL +-- has no previous value and its trailing NULL has no next value. +create table twf(ts datetime, p int, v int); +insert into twf values + ('2026-01-01 00:00:00', 1, 10), ('2026-01-01 00:00:07', 1, null), + ('2026-01-01 00:00:00', 2, null), ('2026-01-01 00:00:07', 2, 40); + +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) order by p, _wstart; +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) fill(prev) order by p, _wstart; +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) fill(next) order by p, _wstart; +select p, _wstart, max(v) from twf group by p interval(ts, 5, second) fill(value, 99) order by p, _wstart; +select p, _wstart, max(v) from twf group by p interval(ts, 10, second) sliding(5, second) fill(prev) order by p, _wstart; + drop database column_pruning;