diff --git a/dgraph/cmd/debug/run.go b/dgraph/cmd/debug/run.go index 37de309258b..7af32335ed2 100644 --- a/dgraph/cmd/debug/run.go +++ b/dgraph/cmd/debug/run.go @@ -112,6 +112,11 @@ func uidToVal(itr *badger.Iterator, prefix string) map[uint64]int { if pk.IsSchema() { continue } + if pk.StartUid > 0 { + // This key is part of a multi-part posting list. Skip it and only read + // the main key, which is the entry point to read the whole list. + continue + } pl, err := posting.ReadPostingList(item.KeyCopy(nil), itr) if err != nil { @@ -501,6 +506,9 @@ func printKeys(db *badger.DB) { if pk.Uid > 0 { fmt.Fprintf(&buf, " uid: %d ", pk.Uid) } + if pk.StartUid > 0 { + fmt.Fprintf(&buf, " startUid: %d ", pk.StartUid) + } fmt.Fprintf(&buf, " key: %s", hex.EncodeToString(item.Key())) if opt.itemMeta { fmt.Fprintf(&buf, " item: [%d, b%04b]", item.EstimatedSize(), item.UserMeta()) diff --git a/posting/index.go b/posting/index.go index 1989c9e9843..a50f1197c34 100644 --- a/posting/index.go +++ b/posting/index.go @@ -527,15 +527,18 @@ func (r *rebuild) Run(ctx context.Context) error { if le == 0 { continue } - kv, err := pl.MarshalToKv() + kvs, err := pl.Rollup() if err != nil { return err } - // We choose to write the PL at r.startTs, so it won't be read by txns, - // which occurred before this schema mutation. Typically, we use - // kv.Version as the timestamp. - if err = writer.SetAt(kv.Key, kv.Value, kv.UserMeta[0], r.startTs); err != nil { - return err + + for _, kv := range kvs { + // We choose to write the PL at r.startTs, so it won't be read by txns, + // which occurred before this schema mutation. Typically, we use + // kv.Version as the timestamp. + if err = writer.SetAt(kv.Key, kv.Value, kv.UserMeta[0], r.startTs); err != nil { + return err + } } // This locking is just to catch any future issues. We shouldn't need // to release this lock, because each posting list must only be accessed diff --git a/posting/list.go b/posting/list.go index 697376e6fa6..3cac5198a29 100644 --- a/posting/list.go +++ b/posting/list.go @@ -50,6 +50,7 @@ var ( ErrInvalidTxn = fmt.Errorf("Invalid transaction") ErrStopIteration = errors.New("Stop iteration") emptyPosting = &pb.Posting{} + maxListSize = MB / 2 ) const ( @@ -84,7 +85,8 @@ func (l *List) maxVersion() uint64 { } type PIterator struct { - pl *pb.PostingList + l *List + plist *pb.PostingList uidPosting *pb.Posting pidx int // index of postings plen int @@ -92,42 +94,153 @@ type PIterator struct { dec *codec.Decoder uids []uint64 uidx int // Offset into the uids slice + + afterUid uint64 + splitIdx int + // The timestamp of a delete marker in the mutable layer. If this value is greater + // than zero, then the immutable posting list should not be traversed. + deleteBelowTs uint64 } -func (it *PIterator) Init(pl *pb.PostingList, afterUid uint64) { - it.pl = pl +func (it *PIterator) Init(l *List, afterUid, deleteBelowTs uint64) error { + if deleteBelowTs > 0 && deleteBelowTs <= l.minTs { + return fmt.Errorf("deleteBelowTs (%d) must be greater than the minTs in the list (%d)", + deleteBelowTs, l.minTs) + } + + it.l = l + it.splitIdx = it.selectInitialSplit(afterUid) + if len(it.l.plist.Splits) > 0 { + plist, err := l.readListPart(it.l.plist.Splits[it.splitIdx]) + if err != nil { + return err + } + it.plist = plist + } else { + it.plist = l.plist + } + + it.afterUid = afterUid + it.deleteBelowTs = deleteBelowTs + it.uidPosting = &pb.Posting{} + it.dec = &codec.Decoder{Pack: it.plist.Pack} + it.uids = it.dec.Seek(it.afterUid, codec.SeekCurrent) + it.uidx = 0 - it.dec = &codec.Decoder{Pack: pl.Pack} + it.plen = len(it.plist.Postings) + it.pidx = sort.Search(it.plen, func(idx int) bool { + p := it.plist.Postings[idx] + return it.afterUid < p.Uid + }) + return nil +} + +func (it *PIterator) selectInitialSplit(afterUid uint64) int { + if afterUid == 0 { + return 0 + } + + for i, startUid := range it.l.plist.Splits { + // If startUid == afterUid, the current block should be selected. + if startUid == afterUid { + return i + } + // If this split starts at an uid greater than afterUid, there might be + // elements in the previous split that need to be checked. + if startUid > afterUid { + return i - 1 + } + } + + // In case no split's startUid is greater or equal than afterUid, start the + // iteration at the start of the last split. + return len(it.l.plist.Splits) - 1 +} + +// moveToNextPart re-initializes the iterator at the start of the next list part. +func (it *PIterator) moveToNextPart() error { + it.splitIdx++ + plist, err := it.l.readListPart(it.l.plist.Splits[it.splitIdx]) + if err != nil { + return err + } + it.plist = plist + + it.dec = &codec.Decoder{Pack: it.plist.Pack} // codec.SeekCurrent makes sure we skip returning afterUid during seek. - it.uids = it.dec.Seek(afterUid, codec.SeekCurrent) + it.uids = it.dec.Seek(it.afterUid, codec.SeekCurrent) it.uidx = 0 - it.plen = len(pl.Postings) + it.plen = len(it.plist.Postings) it.pidx = sort.Search(it.plen, func(idx int) bool { - p := pl.Postings[idx] - return afterUid < p.Uid + p := it.plist.Postings[idx] + return it.afterUid < p.Uid }) + + return nil +} + +// moveToNextValidPart moves the iterator to the next part that contains valid data. +// This is used to skip over parts of the list that might not contain postings. +func (it *PIterator) moveToNextValidPart() error { + // Not a multi-part list, the iterator has reached the end of the list. + if len(it.l.plist.Splits) == 0 { + return nil + } + + // If there are no more uids to iterate over, move to the next part of the + // list that contains valid data. + if len(it.uids) == 0 { + for it.splitIdx <= len(it.l.plist.Splits)-2 { + // moveToNextPart will increment it.splitIdx. Therefore, the for loop must only + // continue until len(splits) - 2. + if err := it.moveToNextPart(); err != nil { + return err + } + + if len(it.uids) > 0 { + return nil + } + } + } + return nil } -func (it *PIterator) Next() { +func (it *PIterator) Next() error { + if it.deleteBelowTs > 0 { + it.uids = nil + return nil + } + it.uidx++ if it.uidx < len(it.uids) { - return + return nil } it.uidx = 0 it.uids = it.dec.Next() + + return it.moveToNextValidPart() } -func (it *PIterator) Valid() bool { - return len(it.uids) > 0 +func (it *PIterator) Valid() (bool, error) { + if len(it.uids) > 0 { + return true, nil + } + + if err := it.moveToNextValidPart(); err != nil { + return false, err + } else if len(it.uids) > 0 { + return true, nil + } + return false, nil } func (it *PIterator) Posting() *pb.Posting { uid := it.uids[it.uidx] for it.pidx < it.plen { - p := it.pl.Postings[it.pidx] + p := it.plist.Postings[it.pidx] if p.Uid > uid { break } @@ -445,7 +558,11 @@ func (l *List) Conflicts(readTs uint64) []uint64 { return conflicts } -func (l *List) pickPostings(readTs uint64) (*pb.PostingList, []*pb.Posting) { +// pickPostings goes through the mutable layer and returns the appropriate postings, +// along with the timestamp of the delete marker, if any. If this timestamp is greater +// than zero, it indicates that the immutable layer should be ignored during traversals. +// If greater than zero, this timestamp must thus be greater than l.minTs. +func (l *List) pickPostings(readTs uint64) (uint64, []*pb.Posting) { // This function would return zero ts for entries above readTs. effective := func(start, commit uint64) uint64 { if commit > 0 && commit <= readTs { @@ -460,16 +577,16 @@ func (l *List) pickPostings(readTs uint64) (*pb.PostingList, []*pb.Posting) { } // First pick up the postings. - var deleteBelow uint64 + var deleteBelowTs uint64 var posts []*pb.Posting for startTs, plist := range l.mutationMap { // Pick up the transactions which are either committed, or the one which is ME. effectiveTs := effective(startTs, plist.CommitTs) - if effectiveTs > deleteBelow { - // We're above the deleteBelow marker. We wouldn't reach here if effectiveTs is zero. + if effectiveTs > deleteBelowTs { + // We're above the deleteBelowTs marker. We wouldn't reach here if effectiveTs is zero. for _, mpost := range plist.Postings { if hasDeleteAll(mpost) { - deleteBelow = effectiveTs + deleteBelowTs = effectiveTs continue } posts = append(posts, mpost) @@ -477,19 +594,12 @@ func (l *List) pickPostings(readTs uint64) (*pb.PostingList, []*pb.Posting) { } } - storedList := l.plist - if deleteBelow > 0 { + if deleteBelowTs > 0 { // There was a delete all marker. So, trim down the list of postings. - - // Create an empty posting list at the same commit ts as the deletion marker. This is - // important, so that after rollup happens, we are left with a posting list at the - // highest commit timestamp. - storedList = &pb.PostingList{CommitTs: deleteBelow} result := posts[:0] - // Trim the posts. for _, post := range posts { effectiveTs := effective(post.StartTs, post.CommitTs) - if effectiveTs < deleteBelow { // Do pick the posts at effectiveTs == deleteBelow. + if effectiveTs < deleteBelowTs { // Do pick the posts at effectiveTs == deleteBelowTs. continue } result = append(result, post) @@ -508,13 +618,13 @@ func (l *List) pickPostings(readTs uint64) (*pb.PostingList, []*pb.Posting) { } return pi.Uid < pj.Uid }) - return storedList, posts + return deleteBelowTs, posts } func (l *List) iterate(readTs uint64, afterUid uint64, f func(obj *pb.Posting) error) error { l.AssertRLock() - plist, mposts := l.pickPostings(readTs) + deleteBelowTs, mposts := l.pickPostings(readTs) if readTs < l.minTs { return x.Errorf("readTs: %d less than minTs: %d for key: %q", readTs, l.minTs, l.key) } @@ -533,14 +643,19 @@ func (l *List) iterate(readTs uint64, afterUid uint64, f func(obj *pb.Posting) e prevUid uint64 err error ) - pitr.Init(plist, afterUid) + err = pitr.Init(l, afterUid, deleteBelowTs) + if err != nil { + return err + } for err == nil { if midx < mlen { mp = mposts[midx] } else { mp = emptyPosting } - if pitr.Valid() { + if valid, err := pitr.Valid(); err != nil { + return err + } else if valid { pp = pitr.Posting() } else { pp = emptyPosting @@ -557,7 +672,9 @@ func (l *List) iterate(readTs uint64, afterUid uint64, f func(obj *pb.Posting) e case mp.Uid == 0 || (pp.Uid > 0 && pp.Uid < mp.Uid): // Either mp is empty, or pp is lower than mp. err = f(pp) - pitr.Next() + if err := pitr.Next(); err != nil { + return err + } case pp.Uid == 0 || (mp.Uid > 0 && mp.Uid < pp.Uid): // Either pp is empty, or mp is lower than pp. if mp.Op != Del { @@ -570,7 +687,9 @@ func (l *List) iterate(readTs uint64, afterUid uint64, f func(obj *pb.Posting) e err = f(mp) } prevUid = mp.Uid - pitr.Next() + if err := pitr.Next(); err != nil { + return err + } midx++ default: log.Fatalf("Unhandled case during iteration of posting list.") @@ -616,26 +735,72 @@ func (l *List) Length(readTs, afterUid uint64) int { return l.length(readTs, afterUid) } -func (l *List) MarshalToKv() (*bpb.KV, error) { - l.Lock() - defer l.Unlock() - if err := l.rollup(math.MaxUint64); err != nil { +// Rollup performs the rollup process, merging the immutable and mutable layers +// and outputting the resulting list so it can be written to disk. +// During this process, the list might be split into multiple lists if the main +// list or any of the existing parts become too big. +// +// A normal list has the following format: +// -> +// +// A multi-part list is stored in multiple keys. The keys for the parts will be generated by +// appending the first uid in the part to the key. The list will have the following format: +// -> +// -> +// -> +// ... +// -> +// +// The first part of a multi-part list always has start uid 1 and will be the last part +// to be deleted, at which point the entire list will be marked for deletion. +// As the list grows, existing parts might be split if they become too big. +func (l *List) Rollup() ([]*bpb.KV, error) { + l.RLock() + defer l.RUnlock() + out, err := l.rollup(math.MaxUint64) + if err != nil { return nil, err } + if out == nil { + return nil, nil + } + var kvs []*bpb.KV kv := &bpb.KV{} - kv.Version = l.minTs + kv.Version = out.newMinTs kv.Key = l.key - val, meta := marshalPostingList(l.plist) + val, meta := marshalPostingList(out.plist) kv.UserMeta = []byte{meta} kv.Value = val - return kv, nil + kvs = append(kvs, kv) + + for startUid, plist := range out.parts { + // Any empty posting list would still have BitEmpty set. And the main posting list + // would NOT have that posting list startUid in the splits list. + kv := out.marshalPostingListPart(l.key, startUid, plist) + kvs = append(kvs, kv) + } + + return kvs, nil } -func marshalPostingList(plist *pb.PostingList) (data []byte, meta byte) { - if plist.Pack == nil || len(plist.Pack.Blocks) == 0 { +func (out *rollupOutput) marshalPostingListPart( + baseKey []byte, startUid uint64, plist *pb.PostingList) *bpb.KV { + kv := &bpb.KV{} + kv.Version = out.newMinTs + kv.Key = x.GetSplitKey(baseKey, startUid) + val, meta := marshalPostingList(plist) + kv.UserMeta = []byte{meta} + kv.Value = val + + return kv +} + +func marshalPostingList(plist *pb.PostingList) ([]byte, byte) { + if isPlistEmpty(plist) { return nil, BitEmptyPosting } + data, err := plist.Marshal() x.Check(err) return data, BitCompletePosting @@ -643,43 +808,81 @@ func marshalPostingList(plist *pb.PostingList) (data []byte, meta byte) { const blockSize int = 256 -func (l *List) Rollup(readTs uint64) error { - l.Lock() - defer l.Unlock() - return l.rollup(readTs) +type rollupOutput struct { + plist *pb.PostingList + parts map[uint64]*pb.PostingList + newMinTs uint64 } // Merge all entries in mutation layer with commitTs <= l.commitTs into // immutable layer. Note that readTs can be math.MaxUint64, so do NOT use it // directly. It should only serve as the read timestamp for iteration. -func (l *List) rollup(readTs uint64) error { - l.AssertLock() +func (l *List) rollup(readTs uint64) (*rollupOutput, error) { + l.AssertRLock() // Pick all committed entries if l.minTs > readTs { // If we are already past the readTs, then skip the rollup. - return nil + return nil, nil + } + + out := &rollupOutput{ + plist: &pb.PostingList{ + Splits: l.plist.Splits, + }, + parts: make(map[uint64]*pb.PostingList), } - final := new(pb.PostingList) - enc := codec.Encoder{BlockSize: blockSize} + var plist *pb.PostingList + var enc codec.Encoder + var startUid, endUid uint64 + var splitIdx int + + // Method to properly initialize all the variables described above. + init := func() { + enc = codec.Encoder{BlockSize: blockSize} + + // If not a multi-part list, all uids go to the same encoder. + if len(l.plist.Splits) == 0 { + plist = out.plist + endUid = math.MaxUint64 + return + } + + // Otherwise, load the corresponding part and set endUid to correctly + // detect the end of the list. + startUid = l.plist.Splits[splitIdx] + if splitIdx+1 == len(l.plist.Splits) { + endUid = math.MaxUint64 + } else { + endUid = l.plist.Splits[splitIdx+1] - 1 + } + + plist = &pb.PostingList{} + } + + init() err := l.iterate(readTs, 0, func(p *pb.Posting) error { - // iterate already takes care of not returning entries whose commitTs is above l.commitTs. - // So, we don't need to do any filtering here. In fact, doing filtering here could result - // in a bug. - enc.Add(p.Uid) + if p.Uid > endUid { + plist.Pack = enc.Done() + out.parts[startUid] = plist - // We want to add the posting if it has facets or has a value. + splitIdx++ + init() + } + + enc.Add(p.Uid) if p.Facets != nil || p.PostingType != pb.Posting_REF || len(p.Label) != 0 { - // I think it's okay to take the pointer from the iterator, because we have a lock - // over List; which won't be released until final has been marshalled. Thus, the - // underlying data wouldn't be changed. - final.Postings = append(final.Postings, p) + plist.Postings = append(plist.Postings, p) } return nil }) + // Finish writing the last part of the list (or the whole list if not a multi-part list). x.Check(err) - final.Pack = enc.Done() + plist.Pack = enc.Done() + if len(l.plist.Splits) > 0 { + out.parts[startUid] = plist + } maxCommitTs := l.minTs { @@ -687,28 +890,21 @@ func (l *List) rollup(readTs uint64) error { // postings which had deletions to provide a sorted view of the list. Therefore, the safest // way to get the max commit timestamp is to pick all the relevant postings for the given // readTs and calculate the maxCommitTs. - plist, mposts := l.pickPostings(readTs) - maxCommitTs = x.Max(maxCommitTs, plist.CommitTs) + // If deleteBelowTs is greater than zero, there was a delete all marker. The list of + // postings has been trimmed down. + deleteBelowTs, mposts := l.pickPostings(readTs) + maxCommitTs = x.Max(maxCommitTs, deleteBelowTs) for _, mp := range mposts { maxCommitTs = x.Max(maxCommitTs, mp.CommitTs) } } - // Keep all uncommitted Entries or postings with commitTs > l.commitTs - // in mutation map. Discard all else. - // TODO: This could be removed after LRU cache is removed. - for startTs, plist := range l.mutationMap { - cl := plist.CommitTs - if cl == 0 || cl > maxCommitTs { - // Keep this. - } else { - delete(l.mutationMap, startTs) - } - } - - l.minTs = maxCommitTs - l.plist = final - return nil + // Check if the list (or any of it's parts if it's been previously split) have + // become too big. Split the list if that is the case. + out.newMinTs = maxCommitTs + out.splitUpList() + out.removeEmptySplits() + return out, nil } func (l *List) ApproxLen() int { @@ -726,7 +922,7 @@ func (l *List) Uids(opt ListOptions) (*pb.List, error) { // Use approximate length for initial capacity. res := make([]uint64, 0, len(l.mutationMap)+codec.ApproxLen(l.plist.Pack)) out := &pb.List{} - if len(l.mutationMap) == 0 && opt.Intersect != nil { + if len(l.mutationMap) == 0 && opt.Intersect != nil && len(l.plist.Splits) == 0 { if opt.ReadTs < l.minTs { l.RUnlock() return out, ErrTsTooOld @@ -963,3 +1159,163 @@ func (l *List) Facets(readTs uint64, param *pb.FacetParams, langs []string) (fs } return facets.CopyFacets(p.Facets, param), nil } + +func (l *List) readListPart(startUid uint64) (*pb.PostingList, error) { + key := x.GetSplitKey(l.key, startUid) + txn := pstore.NewTransactionAt(l.minTs, false) + item, err := txn.Get(key) + if err != nil { + return nil, err + } + part := &pb.PostingList{} + if err := unmarshalOrCopy(part, item); err != nil { + return nil, err + } + return part, nil +} + +// shouldSplit returns true if the given plist should be split in two. +func shouldSplit(plist *pb.PostingList) bool { + return plist.Size() >= maxListSize && len(plist.Pack.Blocks) > 1 +} + +// splitUpList checks the list and splits it in smaller parts if needed. +func (out *rollupOutput) splitUpList() { + // Contains the posting lists that should be split. + var lists []*pb.PostingList + + // If list is not split yet, insert the main list. + if len(out.plist.Splits) == 0 { + lists = append(lists, out.plist) + } + + // Insert the split lists if they exist. + for _, startUid := range out.splits() { + part := out.parts[startUid] + lists = append(lists, part) + } + + // List of startUids for each list part after the splitting process is complete. + var newSplits []uint64 + + for i, list := range lists { + startUid := uint64(1) + // If the list is split, select the right startUid for this list. + if len(out.plist.Splits) > 0 { + startUid = out.plist.Splits[i] + } + + if shouldSplit(list) { + // Split the list. Update out.splits with the new lists and add their + // start uids to the list of new splits. + startUids, pls := binSplit(startUid, list) + for i, startUid := range startUids { + out.parts[startUid] = pls[i] + newSplits = append(newSplits, startUid) + } + } else { + // No need to split the list. Add the startUid to the array of new splits. + newSplits = append(newSplits, startUid) + } + } + + // No new lists were created so there's no need to update the list of splits. + if len(newSplits) == len(lists) { + return + } + + // The splits changed so update them. + out.plist = &pb.PostingList{ + Splits: newSplits, + } +} + +// binSplit takes the given plist and returns two new plists, each with +// half of the blocks and postings of the original as well as the new startUids +// for each of the new parts. +func binSplit(lowUid uint64, plist *pb.PostingList) ([]uint64, []*pb.PostingList) { + midBlock := len(plist.Pack.Blocks) / 2 + midUid := plist.Pack.Blocks[midBlock].GetBase() + + // Generate posting list holding the first half of the current list's postings. + lowPl := new(pb.PostingList) + lowPl.Pack = &pb.UidPack{ + BlockSize: plist.Pack.BlockSize, + Blocks: plist.Pack.Blocks[:midBlock], + } + + // Generate posting list holding the second half of the current list's postings. + highPl := new(pb.PostingList) + highPl.Pack = &pb.UidPack{ + BlockSize: plist.Pack.BlockSize, + Blocks: plist.Pack.Blocks[midBlock:], + } + + // Add elements in plist.Postings to the corresponding list. + for _, posting := range plist.Postings { + if posting.Uid < midUid { + lowPl.Postings = append(lowPl.Postings, posting) + } else { + highPl.Postings = append(highPl.Postings, posting) + } + } + + return []uint64{lowUid, midUid}, []*pb.PostingList{lowPl, highPl} +} + +// removeEmptySplits updates the split list by removing empty posting lists' startUids. +func (out *rollupOutput) removeEmptySplits() { + var splits []uint64 + for startUid, plist := range out.parts { + // Do not remove the first split for now, as every multi-part list should always + // have a split starting with uid 1. + if startUid == 1 { + splits = append(splits, startUid) + continue + } + + if !isPlistEmpty(plist) { + splits = append(splits, startUid) + } + } + out.plist.Splits = splits + sortSplits(splits) + + if len(out.plist.Splits) == 1 { + // Only the first split remains. If it's also empty, remove it as well. + // This should mark the entire list for deletion. + if isPlistEmpty(out.parts[1]) { + out.plist.Splits = []uint64{} + } + } +} + +// Returns the sorted list of start uids based on the keys in out.parts. +// out.parts is considered the source of truth so this method is considered +// safer than using out.plist.Splits directly. +func (out *rollupOutput) splits() []uint64 { + var splits []uint64 + for startUid, _ := range out.parts { + splits = append(splits, startUid) + } + sortSplits(splits) + return splits +} + +// isPlistEmpty returns true if the given plist is empty. Plists with splits are +// considered non-empty. +func isPlistEmpty(plist *pb.PostingList) bool { + if len(plist.Splits) > 0 { + return false + } + if plist.Pack == nil || len(plist.Pack.Blocks) == 0 { + return true + } + return false +} + +func sortSplits(splits []uint64) { + sort.Slice(splits, func(i, j int) bool { + return splits[i] < splits[j] + }) +} diff --git a/posting/list_test.go b/posting/list_test.go index b9766c705cd..8e4e1e746dc 100644 --- a/posting/list_test.go +++ b/posting/list_test.go @@ -22,10 +22,12 @@ import ( "math" "math/rand" "os" + "sort" "strconv" "testing" "github.com/dgraph-io/badger" + bpb "github.com/dgraph-io/badger/pb" "github.com/stretchr/testify/require" "github.com/dgraph-io/dgraph/protos/pb" @@ -230,6 +232,7 @@ func TestAddMutation_DelSet(t *testing.T) { require.EqualValues(t, 1, ol.Length(3, 0)) checkValue(t, ol, "newcars", 3) } + func TestAddMutation_DelRead(t *testing.T) { key := x.DataKey("value", 1543) ol, err := GetNoStore(key) @@ -415,6 +418,9 @@ func TestAddMutation_mrjn1(t *testing.T) { } func TestMillion(t *testing.T) { + // Ensure list is stored in a single part. + maxListSize = math.MaxInt32 + key := x.DataKey("bal", 1331) ol, err := getNew(key, ps) require.NoError(t, err) @@ -431,10 +437,15 @@ func TestMillion(t *testing.T) { // Do a rollup, otherwise, it gets too slow to add a million mutations to one posting // list. t.Logf("Start Ts: %d. Rolling up posting list.\n", txn.StartTs) - require.NoError(t, ol.Rollup(math.MaxUint64)) + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) } commits++ } + t.Logf("Completed a million writes.\n") opt := ListOptions{ReadTs: uint64(N) + 1} l, err := ol.Uids(opt) @@ -852,6 +863,349 @@ func TestAfterUIDCountWithCommit(t *testing.T) { require.EqualValues(t, 0, ol.Length(txn.StartTs, 300)) } +func createMultiPartList(t *testing.T, size int, addLabel bool) (*List, int) { + // For testing, set the max list size to a lower threshold. + maxListSize = 5000 + defer func() { + maxListSize = math.MaxInt32 + }() + + key := x.DataKey("multi-bal", 1331) + ol, err := getNew(key, ps) + require.NoError(t, err) + commits := 0 + for i := 1; i <= size; i++ { + edge := &pb.DirectedEdge{ + ValueId: uint64(i), + } + if addLabel { + edge.Label = strconv.Itoa(i) + } + + txn := Txn{StartTs: uint64(i)} + addMutationHelper(t, ol, edge, Set, &txn) + require.NoError(t, ol.CommitMutation(uint64(i), uint64(i)+1)) + if i%2000 == 0 { + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + } + commits++ + } + + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + + return ol, commits +} + +func createAndDeleteMultiPartList(t *testing.T, size int) (*List, int) { + // For testing, set the max list size to a lower threshold. + maxListSize = 5000 + defer func() { + maxListSize = math.MaxInt32 + }() + + key := x.DataKey("bal_del", 1331) + ol, err := getNew(key, ps) + require.NoError(t, err) + commits := 0 + for i := 1; i <= size; i++ { + edge := &pb.DirectedEdge{ + ValueId: uint64(i), + } + + txn := Txn{StartTs: uint64(i)} + addMutationHelper(t, ol, edge, Set, &txn) + require.NoError(t, ol.CommitMutation(uint64(i), uint64(i)+1)) + if i%2000 == 0 { + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + } + commits++ + } + + // Delete all the previously inserted entries from the list. + baseStartTs := uint64(size) + 1 + for i := 1; i <= size; i++ { + edge := &pb.DirectedEdge{ + ValueId: uint64(i), + } + txn := Txn{StartTs: baseStartTs + uint64(i)} + addMutationHelper(t, ol, edge, Del, &txn) + require.NoError(t, ol.CommitMutation(baseStartTs+uint64(i), baseStartTs+uint64(i)+1)) + if i%2000 == 0 { + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + } + commits++ + } + + return ol, commits +} + +func writePostingListToDisk(kvs []*bpb.KV) error { + writer := NewTxnWriter(pstore) + for _, kv := range kvs { + if err := writer.SetAt(kv.Key, kv.Value, kv.UserMeta[0], kv.Version); err != nil { + return err + } + } + return writer.Flush() +} + +// Create a multi-part list and verify all the uids are there. +func TestMultiPartListBasic(t *testing.T) { + size := int(1e5) + ol, commits := createMultiPartList(t, size, false) + t.Logf("List parts %v", len(ol.plist.Splits)) + opt := ListOptions{ReadTs: uint64(size) + 1} + l, err := ol.Uids(opt) + require.NoError(t, err) + require.Equal(t, commits, len(l.Uids), "List of Uids received: %+v", l.Uids) + for i, uid := range l.Uids { + require.Equal(t, uint64(i+1), uid) + } +} + +// Verify that iteration works with an afterUid value greater than zero. +func TestMultiPartListIterAfterUid(t *testing.T) { + size := int(1e5) + ol, _ := createMultiPartList(t, size, false) + t.Logf("List parts %v", len(ol.plist.Splits)) + + var visitedUids []uint64 + ol.Iterate(uint64(size+1), 50000, func(p *pb.Posting) error { + visitedUids = append(visitedUids, p.Uid) + return nil + }) + require.Equal(t, 50000, len(visitedUids)) + for i, uid := range visitedUids { + require.Equal(t, uint64(50000+i+1), uid) + } +} + +// Verify that postings can be retrieved in multi-part lists. +func TestMultiPartListWithPostings(t *testing.T) { + size := int(1e5) + ol, commits := createMultiPartList(t, size, true) + t.Logf("List parts %v", len(ol.plist.Splits)) + + var labels []string + err := ol.Iterate(uint64(size)+1, 0, func(p *pb.Posting) error { + if len(p.Label) > 0 { + labels = append(labels, p.Label) + } + return nil + }) + require.NoError(t, err) + require.Equal(t, commits, len(labels)) + for i, label := range labels { + require.Equal(t, label, strconv.Itoa(int(i+1))) + } +} + +// Verify marshaling of multi-part lists. +func TestMultiPartListMarshal(t *testing.T) { + size := int(1e5) + ol, _ := createMultiPartList(t, size, false) + t.Logf("List parts %v", len(ol.plist.Splits)) + + kvs, err := ol.Rollup() + require.NoError(t, err) + require.Equal(t, len(kvs), len(ol.plist.Splits)+1) + require.NoError(t, writePostingListToDisk(kvs)) + + sort.Slice(kvs, func(i, j int) bool { + return string(kvs[i].Key) < string(kvs[j].Key) + }) + + key := x.DataKey("multi-bal", 1331) + require.Equal(t, key, kvs[0].Key) + + for i, startUid := range ol.plist.Splits { + partKey := x.GetSplitKey(key, startUid) + require.Equal(t, partKey, kvs[i+1].Key) + part, err := ol.readListPart(startUid) + require.NoError(t, err) + data, err := part.Marshal() + require.NoError(t, err) + require.Equal(t, data, kvs[i+1].Value) + require.Equal(t, []byte{BitCompletePosting}, kvs[i+1].UserMeta) + require.Equal(t, ol.minTs, kvs[i+1].Version) + } +} + +// Verify that writing a multi-part list to disk works correctly. +func TestMultiPartListWriteToDisk(t *testing.T) { + size := int(1e5) + originalList, commits := createMultiPartList(t, size, false) + + kvs, err := originalList.Rollup() + require.NoError(t, err) + require.Equal(t, len(kvs), len(originalList.plist.Splits)+1) + + require.NoError(t, writePostingListToDisk(kvs)) + newList, err := getNew(kvs[0].Key, ps) + + opt := ListOptions{ReadTs: uint64(size) + 1} + originalUids, err := originalList.Uids(opt) + require.NoError(t, err) + newUids, err := newList.Uids(opt) + require.NoError(t, err) + require.Equal(t, commits, len(originalUids.Uids)) + require.Equal(t, len(originalUids.Uids), len(newUids.Uids)) + for i, _ := range originalUids.Uids { + require.Equal(t, originalUids.Uids[i], newUids.Uids[i]) + } +} + +// Verify that adding and deleting all the entries returns an empty list. +func TestMultiPartListDelete(t *testing.T) { + size := int(1e4) + ol, commits := createAndDeleteMultiPartList(t, size) + t.Logf("List parts %v", len(ol.plist.Splits)) + require.Equal(t, size*2, commits) + + counter := 0 + ol.Iterate(math.MaxUint64, 0, func(p *pb.Posting) error { + counter++ + return nil + }) + require.Equal(t, 0, counter) + + kvs, err := ol.Rollup() + require.NoError(t, err) + require.Equal(t, len(kvs), 1) + + for _, kv := range kvs { + require.Equal(t, []byte{BitEmptyPosting}, kv.UserMeta) + require.Equal(t, ol.minTs, kv.Version) + } +} + +// Verify that the first part of a multi-part list is kept even when all its +// entries have been deleted. Do this by creating a list, deleting the first +// half, and ensuring iteration and mutation still work as expected. +func TestMultiPartListDeleteAndAdd(t *testing.T) { + size := int(1e5) + // For testing, set the max list size to a lower threshold. + maxListSize = 5000 + defer func() { + maxListSize = math.MaxInt32 + }() + + // Add entries to the maps. + key := x.DataKey("del_add", 1331) + ol, err := getNew(key, ps) + require.NoError(t, err) + for i := 1; i <= size; i++ { + edge := &pb.DirectedEdge{ + ValueId: uint64(i), + } + + txn := Txn{StartTs: uint64(i)} + addMutationHelper(t, ol, edge, Set, &txn) + require.NoError(t, ol.CommitMutation(uint64(i), uint64(i)+1)) + if i%2000 == 0 { + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + } + } + + // Verify all entries are in the list. + opt := ListOptions{ReadTs: math.MaxUint64} + l, err := ol.Uids(opt) + require.NoError(t, err) + require.Equal(t, size, len(l.Uids), "List of Uids received: %+v", l.Uids) + for i, uid := range l.Uids { + require.Equal(t, uint64(i+1), uid) + } + + // Delete the first half of the previously inserted entries from the list. + baseStartTs := uint64(size) + 1 + for i := 1; i <= 50000; i++ { + edge := &pb.DirectedEdge{ + ValueId: uint64(i), + } + txn := Txn{StartTs: baseStartTs + uint64(i)} + addMutationHelper(t, ol, edge, Del, &txn) + require.NoError(t, ol.CommitMutation(baseStartTs+uint64(i), baseStartTs+uint64(i)+1)) + if i%2000 == 0 { + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + } + } + + // Rollup list at the end of all the deletions. + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + + // Verify that the entries were actually deleted. + opt = ListOptions{ReadTs: math.MaxUint64} + l, err = ol.Uids(opt) + require.NoError(t, err) + require.Equal(t, 50000, len(l.Uids), "List of Uids received: %+v", l.Uids) + for i, uid := range l.Uids { + require.Equal(t, 50000+uint64(i+1), uid) + } + + // Re-add the entries that were just deleted. + baseStartTs = uint64(2*size) + 1 + for i := 1; i <= 50000; i++ { + edge := &pb.DirectedEdge{ + ValueId: uint64(i), + } + txn := Txn{StartTs: baseStartTs + uint64(i)} + addMutationHelper(t, ol, edge, Set, &txn) + require.NoError(t, ol.CommitMutation(baseStartTs+uint64(i), baseStartTs+uint64(i)+1)) + + if i%2000 == 0 { + kvs, err := ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + } + } + + // Rollup list at the end of all the additions + kvs, err = ol.Rollup() + require.NoError(t, err) + require.NoError(t, writePostingListToDisk(kvs)) + ol, err = getNew(key, ps) + require.NoError(t, err) + + // Verify all entries are once again in the list. + opt = ListOptions{ReadTs: math.MaxUint64} + l, err = ol.Uids(opt) + require.NoError(t, err) + require.Equal(t, size, len(l.Uids), "List of Uids received: %+v", l.Uids) + for i, uid := range l.Uids { + require.Equal(t, uint64(i+1), uid) + } +} + var ps *badger.DB func TestMain(m *testing.M) { diff --git a/posting/mvcc.go b/posting/mvcc.go index 34bb5d365f0..d67326142a5 100644 --- a/posting/mvcc.go +++ b/posting/mvcc.go @@ -166,7 +166,6 @@ func (txn *Txn) CommitToMemory(commitTs uint64) error { } func unmarshalOrCopy(plist *pb.PostingList, item *badger.Item) error { - // It's delta return item.Value(func(val []byte) error { if len(val) == 0 { // empty pl @@ -208,8 +207,8 @@ func ReadPostingList(key []byte, it *badger.Iterator) (*List, error) { return nil, err } l.minTs = item.Version() - // No need to do Next here. The outer loop can take care of skipping more versions of - // the same key. + // No need to do Next here. The outer loop can take care of skipping + // more versions of the same key. return l, nil case BitDeltaPosting: err := item.Value(func(val []byte) error { diff --git a/posting/mvcc_test.go b/posting/mvcc_test.go index aaa6a333f0c..39f2a3488be 100644 --- a/posting/mvcc_test.go +++ b/posting/mvcc_test.go @@ -55,10 +55,9 @@ func TestRollupTimestamp(t *testing.T) { // Now check that we don't lost the highest version during a rollup operation, despite the STAR // delete marker being the most recent update. - require.NoError(t, nl.Rollup(11)) - kv, err := nl.MarshalToKv() + kvs, err := nl.Rollup() require.NoError(t, err) - require.Equal(t, uint64(10), kv.Version) + require.Equal(t, uint64(10), kvs[0].Version) } func TestPostingListRead(t *testing.T) { diff --git a/protos/pb.proto b/protos/pb.proto index 9a90d1add4d..297b75e1d35 100644 --- a/protos/pb.proto +++ b/protos/pb.proto @@ -285,6 +285,8 @@ message PostingList { UidPack pack = 1; // Encoded list of uids in this posting list. repeated Posting postings = 2; uint64 commit_ts = 3; // More inclination towards smaller values. + + repeated uint64 splits = 4; } message FacetParam { diff --git a/protos/pb/pb.pb.go b/protos/pb/pb.pb.go index ecf5c13e48e..e5913c010f6 100644 --- a/protos/pb/pb.pb.go +++ b/protos/pb/pb.pb.go @@ -2152,6 +2152,7 @@ type PostingList struct { Pack *UidPack `protobuf:"bytes,1,opt,name=pack,proto3" json:"pack,omitempty"` Postings []*Posting `protobuf:"bytes,2,rep,name=postings,proto3" json:"postings,omitempty"` CommitTs uint64 `protobuf:"varint,3,opt,name=commit_ts,json=commitTs,proto3" json:"commit_ts,omitempty"` + Splits []uint64 `protobuf:"varint,4,rep,packed,name=splits,proto3" json:"splits,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2211,6 +2212,13 @@ func (m *PostingList) GetCommitTs() uint64 { return 0 } +func (m *PostingList) GetSplits() []uint64 { + if m != nil { + return m.Splits + } + return nil +} + type FacetParam struct { Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Alias string `protobuf:"bytes,2,opt,name=alias,proto3" json:"alias,omitempty"` @@ -3740,225 +3748,226 @@ func init() { func init() { proto.RegisterFile("pb.proto", fileDescriptor_f80abaa17e25ccc8) } var fileDescriptor_f80abaa17e25ccc8 = []byte{ - // 3481 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x5a, 0x4f, 0x73, 0x1b, 0x47, + // 3495 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x5a, 0xcd, 0x73, 0x1b, 0x47, 0x76, 0xd7, 0x0c, 0x80, 0xc1, 0xcc, 0x03, 0x48, 0xc1, 0x6d, 0xad, 0x0c, 0x73, 0xd7, 0x12, 0x3d, 0xb6, 0x2c, 0x5a, 0x5e, 0x53, 0x32, 0xbd, 0x49, 0xd6, 0x9b, 0xca, 0x81, 0x12, 0x21, 0x85, 0x36, - 0xff, 0xa5, 0x01, 0x6a, 0x13, 0x57, 0x2a, 0xa8, 0xe6, 0x4c, 0x13, 0x9c, 0xe5, 0x60, 0x66, 0x32, - 0x3d, 0x60, 0x40, 0xdf, 0x72, 0x48, 0xaa, 0xf2, 0x0d, 0xf6, 0x90, 0xca, 0x61, 0x8f, 0xb9, 0x24, - 0xc7, 0x3d, 0x27, 0x95, 0xaa, 0x1c, 0x53, 0xf9, 0x04, 0x29, 0x27, 0x9f, 0x21, 0x55, 0xb9, 0xa5, - 0xde, 0xeb, 0x1e, 0xcc, 0x00, 0xa2, 0x64, 0x3b, 0x55, 0x39, 0x71, 0xde, 0x9f, 0xfe, 0xf7, 0xeb, - 0xf7, 0x5e, 0xbf, 0xf7, 0x40, 0x70, 0xb3, 0xb3, 0xed, 0x2c, 0x4f, 0x8b, 0x94, 0xd9, 0xd9, 0xd9, - 0x86, 0x27, 0xb2, 0x48, 0x93, 0x1b, 0x0f, 0x27, 0x51, 0x71, 0x31, 0x3b, 0xdb, 0x0e, 0xd2, 0xe9, - 0xe3, 0x70, 0x92, 0x8b, 0xec, 0xe2, 0xd3, 0x28, 0x7d, 0x7c, 0x26, 0xc2, 0x89, 0xcc, 0x1f, 0x67, - 0x67, 0x8f, 0xcb, 0x71, 0xfe, 0x06, 0x34, 0x0f, 0x22, 0x55, 0x30, 0x06, 0xcd, 0x59, 0x14, 0xaa, - 0xbe, 0xb5, 0xd9, 0xd8, 0x72, 0x38, 0x7d, 0xfb, 0x87, 0xe0, 0x8d, 0x84, 0xba, 0x7c, 0x29, 0xe2, - 0x99, 0x64, 0x3d, 0x68, 0x5c, 0x89, 0xb8, 0x6f, 0x6d, 0x5a, 0x5b, 0x5d, 0x8e, 0x9f, 0x6c, 0x1b, - 0xdc, 0x2b, 0x11, 0x8f, 0x8b, 0xeb, 0x4c, 0xf6, 0xed, 0x4d, 0x6b, 0x6b, 0x7d, 0xe7, 0xed, 0xed, - 0xec, 0x6c, 0xfb, 0x24, 0x55, 0x45, 0x94, 0x4c, 0xb6, 0x5f, 0x8a, 0x78, 0x74, 0x9d, 0x49, 0xde, - 0xbe, 0xd2, 0x1f, 0xfe, 0x31, 0x74, 0x86, 0x79, 0xf0, 0x7c, 0x96, 0x04, 0x45, 0x94, 0x26, 0xb8, - 0x62, 0x22, 0xa6, 0x92, 0x66, 0xf4, 0x38, 0x7d, 0x23, 0x4f, 0xe4, 0x13, 0xd5, 0x6f, 0x6c, 0x36, - 0x90, 0x87, 0xdf, 0xac, 0x0f, 0xed, 0x48, 0x3d, 0x4b, 0x67, 0x49, 0xd1, 0x6f, 0x6e, 0x5a, 0x5b, - 0x2e, 0x2f, 0x49, 0xff, 0x6f, 0x1a, 0xd0, 0xfa, 0xa3, 0x99, 0xcc, 0xaf, 0x69, 0x5c, 0x51, 0xe4, - 0xe5, 0x5c, 0xf8, 0xcd, 0xee, 0x40, 0x2b, 0x16, 0xc9, 0x44, 0xf5, 0x6d, 0x9a, 0x4c, 0x13, 0xec, - 0xc7, 0xe0, 0x89, 0xf3, 0x42, 0xe6, 0xe3, 0x59, 0x14, 0xf6, 0x1b, 0x9b, 0xd6, 0x96, 0xc3, 0x5d, - 0x62, 0x9c, 0x46, 0x21, 0x7b, 0x17, 0xdc, 0x30, 0x1d, 0x07, 0xf5, 0xb5, 0xc2, 0x94, 0xd6, 0x62, - 0x1f, 0x80, 0x3b, 0x8b, 0xc2, 0x71, 0x1c, 0xa9, 0xa2, 0xdf, 0xda, 0xb4, 0xb6, 0x3a, 0x3b, 0x2e, - 0x1e, 0x16, 0xb1, 0xe3, 0xed, 0x59, 0x14, 0x12, 0x88, 0x8f, 0xc0, 0x55, 0x79, 0x30, 0x3e, 0x9f, - 0x25, 0x41, 0xdf, 0x21, 0xa5, 0xdb, 0xa8, 0x54, 0x3b, 0x35, 0x6f, 0x2b, 0x4d, 0xe0, 0xb1, 0x72, - 0x79, 0x25, 0x73, 0x25, 0xfb, 0x6d, 0xbd, 0x94, 0x21, 0xd9, 0x13, 0xe8, 0x9c, 0x8b, 0x40, 0x16, - 0xe3, 0x4c, 0xe4, 0x62, 0xda, 0x77, 0xab, 0x89, 0x9e, 0x23, 0xfb, 0x04, 0xb9, 0x8a, 0xc3, 0xf9, - 0x82, 0x60, 0x9f, 0xc3, 0x1a, 0x51, 0x6a, 0x7c, 0x1e, 0xc5, 0x85, 0xcc, 0xfb, 0x1e, 0x8d, 0x59, - 0xa7, 0x31, 0xc4, 0x19, 0xe5, 0x52, 0xf2, 0xae, 0x56, 0xd2, 0x1c, 0xf6, 0x1e, 0x80, 0x9c, 0x67, - 0x22, 0x09, 0xc7, 0x22, 0x8e, 0xfb, 0x40, 0x7b, 0xf0, 0x34, 0x67, 0x37, 0x8e, 0xd9, 0x3b, 0xb8, - 0x3f, 0x11, 0x8e, 0x0b, 0xd5, 0x5f, 0xdb, 0xb4, 0xb6, 0x9a, 0xdc, 0x41, 0x72, 0xa4, 0x10, 0xd7, - 0x40, 0x04, 0x17, 0xb2, 0xbf, 0xbe, 0x69, 0x6d, 0xb5, 0xb8, 0x26, 0xfc, 0x1d, 0xf0, 0xc8, 0x4e, - 0x08, 0x87, 0x07, 0xe0, 0x5c, 0x21, 0xa1, 0xcd, 0xa9, 0xb3, 0xb3, 0x86, 0x1b, 0x59, 0x98, 0x12, - 0x37, 0x42, 0xff, 0x1e, 0xb8, 0x07, 0x22, 0x99, 0x94, 0xf6, 0x87, 0x17, 0x44, 0x03, 0x3c, 0x4e, - 0xdf, 0xfe, 0xaf, 0x6d, 0x70, 0xb8, 0x54, 0xb3, 0xb8, 0x60, 0x0f, 0x01, 0x10, 0xfe, 0xa9, 0x28, - 0xf2, 0x68, 0x6e, 0x66, 0xad, 0x2e, 0xc0, 0x9b, 0x45, 0xe1, 0x21, 0x89, 0xd8, 0x13, 0xe8, 0xd2, - 0xec, 0xa5, 0xaa, 0x5d, 0x6d, 0x60, 0xb1, 0x3f, 0xde, 0x21, 0x15, 0x33, 0xe2, 0x2e, 0x38, 0x74, - 0xe3, 0xda, 0xea, 0xd6, 0xb8, 0xa1, 0xd8, 0x03, 0x58, 0x8f, 0x92, 0x02, 0x6f, 0x24, 0x28, 0xc6, - 0xa1, 0x54, 0xa5, 0x49, 0xac, 0x2d, 0xb8, 0x7b, 0x52, 0x15, 0xec, 0x33, 0xd0, 0xb0, 0x96, 0x0b, - 0xb6, 0x68, 0xc1, 0xf5, 0xc5, 0x75, 0x29, 0xbd, 0x22, 0xe9, 0x98, 0x15, 0x3f, 0x85, 0x0e, 0x9e, - 0xaf, 0x1c, 0xe1, 0xd0, 0x88, 0x2e, 0x9d, 0xc6, 0xc0, 0xc1, 0x01, 0x15, 0x8c, 0x3a, 0x42, 0x83, - 0x66, 0xa7, 0xcd, 0x84, 0xbe, 0xfd, 0x01, 0xb4, 0x8e, 0xf3, 0x50, 0xe6, 0x37, 0x5a, 0x3e, 0x83, - 0x66, 0x28, 0x55, 0x40, 0x4e, 0xe9, 0x72, 0xfa, 0xae, 0xbc, 0xa1, 0x51, 0xf3, 0x06, 0xff, 0xef, - 0x2c, 0xe8, 0x0c, 0xd3, 0xbc, 0x38, 0x94, 0x4a, 0x89, 0x89, 0x64, 0xf7, 0xa1, 0x95, 0xe2, 0xb4, - 0x06, 0x61, 0x0f, 0xf7, 0x44, 0xeb, 0x70, 0xcd, 0x5f, 0xb9, 0x07, 0xfb, 0xf5, 0xf7, 0x80, 0x56, - 0x42, 0x7e, 0xd4, 0x30, 0x56, 0x42, 0x5e, 0x74, 0x17, 0x9c, 0xf4, 0xfc, 0x5c, 0x49, 0x8d, 0x65, - 0x8b, 0x1b, 0xea, 0xb5, 0xc6, 0xe6, 0xff, 0x0e, 0x00, 0xee, 0xef, 0x07, 0x5a, 0x81, 0x7f, 0x01, - 0x1d, 0x2e, 0xce, 0x8b, 0x67, 0x69, 0x52, 0xc8, 0x79, 0xc1, 0xd6, 0xc1, 0x8e, 0x42, 0x82, 0xc8, - 0xe1, 0x76, 0x14, 0xe2, 0xe6, 0x26, 0x79, 0x3a, 0xcb, 0x08, 0xa1, 0x35, 0xae, 0x09, 0x82, 0x32, - 0x0c, 0x73, 0xda, 0x31, 0x42, 0x19, 0x86, 0x39, 0xbb, 0x0f, 0x1d, 0x95, 0x88, 0x4c, 0x5d, 0xa4, - 0x05, 0x6e, 0xae, 0x49, 0x9b, 0x83, 0x92, 0x35, 0x52, 0xfe, 0xbf, 0x58, 0xe0, 0x1c, 0xca, 0xe9, - 0x99, 0xcc, 0x5f, 0x59, 0xe5, 0x5d, 0x70, 0x69, 0xe2, 0x71, 0x14, 0x9a, 0x85, 0xda, 0x44, 0xef, - 0x87, 0x37, 0x2e, 0x75, 0x17, 0x9c, 0x58, 0x0a, 0x04, 0x5f, 0xdb, 0x99, 0xa1, 0x10, 0x1b, 0x31, - 0x1d, 0x87, 0x52, 0x84, 0x14, 0x78, 0x5c, 0xee, 0x88, 0xe9, 0x9e, 0x14, 0x21, 0xee, 0x2d, 0x16, - 0xaa, 0x18, 0xcf, 0xb2, 0x50, 0x14, 0x92, 0x02, 0x4e, 0x13, 0x0d, 0x47, 0x15, 0xa7, 0xc4, 0x61, - 0x8f, 0xe0, 0xad, 0x20, 0x9e, 0x29, 0x8c, 0x76, 0x51, 0x72, 0x9e, 0x8e, 0xd3, 0x24, 0xbe, 0x26, - 0x7c, 0x5d, 0x7e, 0xdb, 0x08, 0xf6, 0x93, 0xf3, 0xf4, 0x38, 0x89, 0xaf, 0xfd, 0xdf, 0xda, 0xd0, - 0x7a, 0x41, 0x30, 0x3c, 0x81, 0xf6, 0x94, 0x0e, 0x54, 0x7a, 0xef, 0x5d, 0x44, 0x98, 0x64, 0xdb, - 0xfa, 0xa4, 0x6a, 0x90, 0x14, 0xf9, 0x35, 0x2f, 0xd5, 0x70, 0x44, 0x21, 0xce, 0x62, 0x59, 0x28, - 0x63, 0x11, 0xb5, 0x11, 0x23, 0x2d, 0x30, 0x23, 0x8c, 0xda, 0x2a, 0xac, 0x8d, 0x55, 0x58, 0xd9, - 0x06, 0xb8, 0xc1, 0x85, 0x0c, 0x2e, 0xd5, 0x6c, 0x6a, 0x40, 0x5f, 0xd0, 0x1b, 0xcf, 0xa1, 0x5b, - 0xdf, 0x07, 0xbe, 0x4c, 0x97, 0xf2, 0x9a, 0x80, 0x6f, 0x72, 0xfc, 0x64, 0x9b, 0xd0, 0x22, 0x0f, - 0x27, 0xd8, 0x3b, 0x3b, 0x80, 0xdb, 0xd1, 0x43, 0xb8, 0x16, 0xfc, 0xc2, 0xfe, 0xb9, 0x85, 0xf3, - 0xd4, 0x77, 0x57, 0x9f, 0xc7, 0x7b, 0xfd, 0x3c, 0x7a, 0x48, 0x6d, 0x1e, 0xff, 0x7f, 0x6c, 0xe8, + 0xbf, 0xd2, 0x00, 0xb5, 0x89, 0x2b, 0x15, 0x54, 0x73, 0xa6, 0x09, 0xce, 0x72, 0x30, 0x33, 0x99, + 0x1e, 0x30, 0xa0, 0x6f, 0x39, 0x24, 0x55, 0xa9, 0xca, 0x1f, 0xb0, 0x87, 0x54, 0x0e, 0x7b, 0xcc, + 0x25, 0x39, 0xee, 0x39, 0xa9, 0x54, 0xe5, 0x98, 0xca, 0x5f, 0x90, 0x72, 0xf2, 0x37, 0xa4, 0x2a, + 0xb7, 0xd4, 0x7b, 0xdd, 0x83, 0x19, 0x40, 0x94, 0xbc, 0x4e, 0xd5, 0x9e, 0x38, 0xef, 0xa3, 0xbf, + 0x7e, 0xfd, 0xbe, 0xfa, 0x81, 0xe0, 0x66, 0x67, 0xdb, 0x59, 0x9e, 0x16, 0x29, 0xb3, 0xb3, 0xb3, + 0x0d, 0x4f, 0x64, 0x91, 0x26, 0x37, 0x1e, 0x4e, 0xa2, 0xe2, 0x62, 0x76, 0xb6, 0x1d, 0xa4, 0xd3, + 0xc7, 0xe1, 0x24, 0x17, 0xd9, 0xc5, 0xa7, 0x51, 0xfa, 0xf8, 0x4c, 0x84, 0x13, 0x99, 0x3f, 0xce, + 0xce, 0x1e, 0x97, 0xe3, 0xfc, 0x0d, 0x68, 0x1e, 0x44, 0xaa, 0x60, 0x0c, 0x9a, 0xb3, 0x28, 0x54, + 0x7d, 0x6b, 0xb3, 0xb1, 0xe5, 0x70, 0xfa, 0xf6, 0x0f, 0xc1, 0x1b, 0x09, 0x75, 0xf9, 0x52, 0xc4, + 0x33, 0xc9, 0x7a, 0xd0, 0xb8, 0x12, 0x71, 0xdf, 0xda, 0xb4, 0xb6, 0xba, 0x1c, 0x3f, 0xd9, 0x36, + 0xb8, 0x57, 0x22, 0x1e, 0x17, 0xd7, 0x99, 0xec, 0xdb, 0x9b, 0xd6, 0xd6, 0xfa, 0xce, 0xdb, 0xdb, + 0xd9, 0xd9, 0xf6, 0x49, 0xaa, 0x8a, 0x28, 0x99, 0x6c, 0xbf, 0x14, 0xf1, 0xe8, 0x3a, 0x93, 0xbc, + 0x7d, 0xa5, 0x3f, 0xfc, 0x63, 0xe8, 0x0c, 0xf3, 0xe0, 0xf9, 0x2c, 0x09, 0x8a, 0x28, 0x4d, 0x70, + 0xc5, 0x44, 0x4c, 0x25, 0xcd, 0xe8, 0x71, 0xfa, 0x46, 0x9e, 0xc8, 0x27, 0xaa, 0xdf, 0xd8, 0x6c, + 0x20, 0x0f, 0xbf, 0x59, 0x1f, 0xda, 0x91, 0x7a, 0x96, 0xce, 0x92, 0xa2, 0xdf, 0xdc, 0xb4, 0xb6, + 0x5c, 0x5e, 0x92, 0xfe, 0xdf, 0x34, 0xa0, 0xf5, 0x47, 0x33, 0x99, 0x5f, 0xd3, 0xb8, 0xa2, 0xc8, + 0xcb, 0xb9, 0xf0, 0x9b, 0xdd, 0x81, 0x56, 0x2c, 0x92, 0x89, 0xea, 0xdb, 0x34, 0x99, 0x26, 0xd8, + 0x0f, 0xc1, 0x13, 0xe7, 0x85, 0xcc, 0xc7, 0xb3, 0x28, 0xec, 0x37, 0x36, 0xad, 0x2d, 0x87, 0xbb, + 0xc4, 0x38, 0x8d, 0x42, 0xf6, 0x2e, 0xb8, 0x61, 0x3a, 0x0e, 0xea, 0x6b, 0x85, 0x29, 0xad, 0xc5, + 0x3e, 0x00, 0x77, 0x16, 0x85, 0xe3, 0x38, 0x52, 0x45, 0xbf, 0xb5, 0x69, 0x6d, 0x75, 0x76, 0x5c, + 0x3c, 0x2c, 0x62, 0xc7, 0xdb, 0xb3, 0x28, 0x24, 0x10, 0x1f, 0x81, 0xab, 0xf2, 0x60, 0x7c, 0x3e, + 0x4b, 0x82, 0xbe, 0x43, 0x4a, 0xb7, 0x51, 0xa9, 0x76, 0x6a, 0xde, 0x56, 0x9a, 0xc0, 0x63, 0xe5, + 0xf2, 0x4a, 0xe6, 0x4a, 0xf6, 0xdb, 0x7a, 0x29, 0x43, 0xb2, 0x27, 0xd0, 0x39, 0x17, 0x81, 0x2c, + 0xc6, 0x99, 0xc8, 0xc5, 0xb4, 0xef, 0x56, 0x13, 0x3d, 0x47, 0xf6, 0x09, 0x72, 0x15, 0x87, 0xf3, + 0x05, 0xc1, 0x3e, 0x87, 0x35, 0xa2, 0xd4, 0xf8, 0x3c, 0x8a, 0x0b, 0x99, 0xf7, 0x3d, 0x1a, 0xb3, + 0x4e, 0x63, 0x88, 0x33, 0xca, 0xa5, 0xe4, 0x5d, 0xad, 0xa4, 0x39, 0xec, 0x3d, 0x00, 0x39, 0xcf, + 0x44, 0x12, 0x8e, 0x45, 0x1c, 0xf7, 0x81, 0xf6, 0xe0, 0x69, 0xce, 0x6e, 0x1c, 0xb3, 0x77, 0x70, + 0x7f, 0x22, 0x1c, 0x17, 0xaa, 0xbf, 0xb6, 0x69, 0x6d, 0x35, 0xb9, 0x83, 0xe4, 0x48, 0x21, 0xae, + 0x81, 0x08, 0x2e, 0x64, 0x7f, 0x7d, 0xd3, 0xda, 0x6a, 0x71, 0x4d, 0xf8, 0x3b, 0xe0, 0x91, 0x9d, + 0x10, 0x0e, 0x0f, 0xc0, 0xb9, 0x42, 0x42, 0x9b, 0x53, 0x67, 0x67, 0x0d, 0x37, 0xb2, 0x30, 0x25, + 0x6e, 0x84, 0xfe, 0x3d, 0x70, 0x0f, 0x44, 0x32, 0x29, 0xed, 0x0f, 0x2f, 0x88, 0x06, 0x78, 0x9c, + 0xbe, 0xfd, 0x5f, 0xda, 0xe0, 0x70, 0xa9, 0x66, 0x71, 0xc1, 0x1e, 0x02, 0x20, 0xfc, 0x53, 0x51, + 0xe4, 0xd1, 0xdc, 0xcc, 0x5a, 0x5d, 0x80, 0x37, 0x8b, 0xc2, 0x43, 0x12, 0xb1, 0x27, 0xd0, 0xa5, + 0xd9, 0x4b, 0x55, 0xbb, 0xda, 0xc0, 0x62, 0x7f, 0xbc, 0x43, 0x2a, 0x66, 0xc4, 0x5d, 0x70, 0xe8, + 0xc6, 0xb5, 0xd5, 0xad, 0x71, 0x43, 0xb1, 0x07, 0xb0, 0x1e, 0x25, 0x05, 0xde, 0x48, 0x50, 0x8c, + 0x43, 0xa9, 0x4a, 0x93, 0x58, 0x5b, 0x70, 0xf7, 0xa4, 0x2a, 0xd8, 0x67, 0xa0, 0x61, 0x2d, 0x17, + 0x6c, 0xd1, 0x82, 0xeb, 0x8b, 0xeb, 0x52, 0x7a, 0x45, 0xd2, 0x31, 0x2b, 0x7e, 0x0a, 0x1d, 0x3c, + 0x5f, 0x39, 0xc2, 0xa1, 0x11, 0x5d, 0x3a, 0x8d, 0x81, 0x83, 0x03, 0x2a, 0x18, 0x75, 0x84, 0x06, + 0xcd, 0x4e, 0x9b, 0x09, 0x7d, 0xfb, 0x03, 0x68, 0x1d, 0xe7, 0xa1, 0xcc, 0x6f, 0xb4, 0x7c, 0x06, + 0xcd, 0x50, 0xaa, 0x80, 0x9c, 0xd2, 0xe5, 0xf4, 0x5d, 0x79, 0x43, 0xa3, 0xe6, 0x0d, 0xfe, 0xdf, + 0x5b, 0xd0, 0x19, 0xa6, 0x79, 0x71, 0x28, 0x95, 0x12, 0x13, 0xc9, 0xee, 0x43, 0x2b, 0xc5, 0x69, + 0x0d, 0xc2, 0x1e, 0xee, 0x89, 0xd6, 0xe1, 0x9a, 0xbf, 0x72, 0x0f, 0xf6, 0xeb, 0xef, 0x01, 0xad, + 0x84, 0xfc, 0xa8, 0x61, 0xac, 0x84, 0xbc, 0xe8, 0x2e, 0x38, 0xe9, 0xf9, 0xb9, 0x92, 0x1a, 0xcb, + 0x16, 0x37, 0xd4, 0x6b, 0x8d, 0xcd, 0xff, 0x1d, 0x00, 0xdc, 0xdf, 0xf7, 0xb4, 0x02, 0xff, 0x02, + 0x3a, 0x5c, 0x9c, 0x17, 0xcf, 0xd2, 0xa4, 0x90, 0xf3, 0x82, 0xad, 0x83, 0x1d, 0x85, 0x04, 0x91, + 0xc3, 0xed, 0x28, 0xc4, 0xcd, 0x4d, 0xf2, 0x74, 0x96, 0x11, 0x42, 0x6b, 0x5c, 0x13, 0x04, 0x65, + 0x18, 0xe6, 0xb4, 0x63, 0x84, 0x32, 0x0c, 0x73, 0x76, 0x1f, 0x3a, 0x2a, 0x11, 0x99, 0xba, 0x48, + 0x0b, 0xdc, 0x5c, 0x93, 0x36, 0x07, 0x25, 0x6b, 0xa4, 0xfc, 0x7f, 0xb5, 0xc0, 0x39, 0x94, 0xd3, + 0x33, 0x99, 0xbf, 0xb2, 0xca, 0xbb, 0xe0, 0xd2, 0xc4, 0xe3, 0x28, 0x34, 0x0b, 0xb5, 0x89, 0xde, + 0x0f, 0x6f, 0x5c, 0xea, 0x2e, 0x38, 0xb1, 0x14, 0x08, 0xbe, 0xb6, 0x33, 0x43, 0x21, 0x36, 0x62, + 0x3a, 0x0e, 0xa5, 0x08, 0x29, 0xf0, 0xb8, 0xdc, 0x11, 0xd3, 0x3d, 0x29, 0x42, 0xdc, 0x5b, 0x2c, + 0x54, 0x31, 0x9e, 0x65, 0xa1, 0x28, 0x24, 0x05, 0x9c, 0x26, 0x1a, 0x8e, 0x2a, 0x4e, 0x89, 0xc3, + 0x1e, 0xc1, 0x5b, 0x41, 0x3c, 0x53, 0x18, 0xed, 0xa2, 0xe4, 0x3c, 0x1d, 0xa7, 0x49, 0x7c, 0x4d, + 0xf8, 0xba, 0xfc, 0xb6, 0x11, 0xec, 0x27, 0xe7, 0xe9, 0x71, 0x12, 0x5f, 0xfb, 0xbf, 0xb6, 0xa1, + 0xf5, 0x82, 0x60, 0x78, 0x02, 0xed, 0x29, 0x1d, 0xa8, 0xf4, 0xde, 0xbb, 0x88, 0x30, 0xc9, 0xb6, + 0xf5, 0x49, 0xd5, 0x20, 0x29, 0xf2, 0x6b, 0x5e, 0xaa, 0xe1, 0x88, 0x42, 0x9c, 0xc5, 0xb2, 0x50, + 0xc6, 0x22, 0x6a, 0x23, 0x46, 0x5a, 0x60, 0x46, 0x18, 0xb5, 0x55, 0x58, 0x1b, 0xab, 0xb0, 0xb2, + 0x0d, 0x70, 0x83, 0x0b, 0x19, 0x5c, 0xaa, 0xd9, 0xd4, 0x80, 0xbe, 0xa0, 0x37, 0x9e, 0x43, 0xb7, + 0xbe, 0x0f, 0xcc, 0x4c, 0x97, 0xf2, 0x9a, 0x80, 0x6f, 0x72, 0xfc, 0x64, 0x9b, 0xd0, 0x22, 0x0f, + 0x27, 0xd8, 0x3b, 0x3b, 0x80, 0xdb, 0xd1, 0x43, 0xb8, 0x16, 0xfc, 0xcc, 0xfe, 0xa9, 0x85, 0xf3, + 0xd4, 0x77, 0x57, 0x9f, 0xc7, 0x7b, 0xfd, 0x3c, 0x7a, 0x48, 0x6d, 0x1e, 0xff, 0x7f, 0x6d, 0xe8, 0x7e, 0x2d, 0xf3, 0xf4, 0x24, 0x4f, 0xb3, 0x54, 0x89, 0x98, 0xed, 0x2e, 0x9f, 0x4e, 0xa3, 0xb8, 0x89, 0x83, 0xeb, 0x6a, 0xdb, 0xc3, 0xc5, 0x71, 0x35, 0x3a, 0xf5, 0xf3, 0xfb, 0xe0, 0x68, 0x74, 0x6f, 0x38, 0x82, 0x91, 0xa0, 0x8e, 0xc6, 0x93, 0xf0, 0x5b, 0xde, 0x9e, 0x91, 0xb0, 0x7b, 0x00, 0x53, 0x31, 0x3f, 0x90, 0x42, 0xc9, 0xfd, 0xb0, 0x34, 0xdf, 0x8a, 0x83, 0x38, 0x4f, 0xc5, 0x7c, - 0x34, 0x4f, 0x46, 0x8a, 0xac, 0xab, 0xc9, 0x17, 0x34, 0xfb, 0x09, 0x78, 0x53, 0x31, 0x47, 0x3f, - 0xda, 0x0f, 0x8d, 0x75, 0x55, 0x0c, 0xf6, 0x3e, 0x34, 0x8a, 0x79, 0x42, 0x41, 0x09, 0x5f, 0x27, - 0x4c, 0x3d, 0x46, 0xf3, 0xc4, 0x78, 0x1c, 0x47, 0x59, 0x09, 0xa8, 0x5b, 0x01, 0xda, 0x83, 0x46, - 0x10, 0x85, 0xf4, 0x3c, 0x79, 0x1c, 0x3f, 0x37, 0xfe, 0x00, 0x6e, 0xaf, 0xe0, 0x50, 0xbf, 0x87, - 0x35, 0x3d, 0xec, 0x4e, 0xfd, 0x1e, 0x9a, 0x75, 0xec, 0x7f, 0xdb, 0x80, 0xdb, 0xc6, 0x18, 0x2e, - 0xa2, 0x6c, 0x58, 0xa0, 0xd9, 0xf7, 0xa1, 0x4d, 0xd1, 0x46, 0xe6, 0xc6, 0x26, 0x4a, 0x92, 0xfd, - 0x1e, 0x38, 0xe4, 0x81, 0xa5, 0x9d, 0xde, 0xaf, 0x50, 0x5d, 0x0c, 0xd7, 0x76, 0x6b, 0xae, 0xc4, - 0xa8, 0xb3, 0x9f, 0x41, 0xeb, 0x1b, 0x99, 0xa7, 0x3a, 0x7a, 0x76, 0x76, 0xee, 0xdd, 0x34, 0x0e, - 0xef, 0xd6, 0x0c, 0xd3, 0xca, 0xff, 0x8f, 0xe0, 0x7f, 0x88, 0xf1, 0x72, 0x9a, 0x5e, 0xc9, 0xb0, - 0xdf, 0xa6, 0x1d, 0xd5, 0xed, 0xa3, 0x14, 0x95, 0x68, 0xbb, 0x15, 0xda, 0x7b, 0xd0, 0xa9, 0x1d, - 0xef, 0x06, 0xa4, 0xef, 0x2f, 0x5b, 0xbc, 0xb7, 0x70, 0xe4, 0xba, 0xe3, 0xec, 0x01, 0x54, 0x87, - 0xfd, 0xbf, 0xba, 0x9f, 0xff, 0x97, 0x16, 0xdc, 0x7e, 0x96, 0x26, 0x89, 0xa4, 0xc4, 0x48, 0x5f, - 0x5d, 0x65, 0xf6, 0xd6, 0x6b, 0xcd, 0xfe, 0x63, 0x68, 0x29, 0x54, 0x36, 0xb3, 0xbf, 0x7d, 0xc3, - 0x5d, 0x70, 0xad, 0x81, 0x61, 0x66, 0x2a, 0xe6, 0xe3, 0x4c, 0x26, 0x61, 0x94, 0x4c, 0xca, 0x30, - 0x33, 0x15, 0xf3, 0x13, 0xcd, 0xf1, 0x7f, 0x63, 0x81, 0xa3, 0x3d, 0x66, 0x29, 0x5a, 0x5b, 0xcb, - 0xd1, 0xfa, 0x27, 0xe0, 0x65, 0xb9, 0x0c, 0xa3, 0xa0, 0x5c, 0xd5, 0xe3, 0x15, 0x03, 0x8d, 0xf3, - 0x3c, 0xcd, 0x03, 0x49, 0xd3, 0xbb, 0x5c, 0x13, 0xc8, 0x55, 0x99, 0x08, 0x74, 0x72, 0xd7, 0xe0, - 0x9a, 0xc0, 0x18, 0xaf, 0x2f, 0x87, 0x2e, 0xc5, 0xe5, 0x86, 0xc2, 0xac, 0x94, 0xde, 0x3f, 0x8a, - 0xd0, 0x1e, 0x89, 0x5c, 0x64, 0x50, 0x68, 0xfe, 0x7b, 0x1b, 0xba, 0x7b, 0x51, 0x2e, 0x83, 0x42, - 0x86, 0x83, 0x70, 0x42, 0xb3, 0xc8, 0xa4, 0x88, 0x8a, 0x6b, 0xf3, 0xd8, 0x18, 0x6a, 0x91, 0x0b, - 0xd8, 0xcb, 0x59, 0xb0, 0xbe, 0x8b, 0x06, 0x25, 0xee, 0x9a, 0x60, 0x3b, 0x00, 0x3a, 0x4b, 0xa2, - 0xe4, 0xbd, 0xf9, 0xfa, 0xe4, 0xdd, 0x23, 0x35, 0xfc, 0x44, 0x80, 0xf4, 0x98, 0x48, 0x3f, 0x44, - 0x0e, 0x65, 0xf6, 0x33, 0x34, 0x64, 0x4a, 0x2e, 0xce, 0x64, 0x4c, 0x86, 0x4a, 0xc9, 0xc5, 0x99, - 0x8c, 0x17, 0x29, 0x5d, 0x5b, 0x6f, 0x07, 0xbf, 0xd9, 0x07, 0x60, 0xa7, 0x19, 0x1d, 0xde, 0x2c, - 0x58, 0x3f, 0xd8, 0xf6, 0x71, 0xc6, 0xed, 0x34, 0x43, 0x2b, 0xd0, 0x99, 0x6a, 0xdf, 0x33, 0xc6, - 0x8d, 0xd1, 0x85, 0xb2, 0x29, 0x6e, 0x24, 0xfe, 0x5d, 0xb0, 0x8f, 0x33, 0xd6, 0x86, 0xc6, 0x70, - 0x30, 0xea, 0xdd, 0xc2, 0x8f, 0xbd, 0xc1, 0x41, 0xcf, 0xf2, 0xff, 0xd9, 0x06, 0xef, 0x70, 0x56, - 0x08, 0xb4, 0x29, 0xf5, 0xa6, 0x4b, 0x7d, 0x17, 0x5c, 0x55, 0x88, 0x9c, 0x22, 0xb4, 0x0e, 0x2b, - 0x6d, 0xa2, 0x47, 0x8a, 0x7d, 0x04, 0x2d, 0x19, 0x4e, 0x64, 0xe9, 0xed, 0xbd, 0xd5, 0x7d, 0x72, - 0x2d, 0x66, 0x5b, 0xe0, 0xa8, 0xe0, 0x42, 0x4e, 0x45, 0xbf, 0x59, 0x29, 0x0e, 0x89, 0xa3, 0x5f, - 0x60, 0x6e, 0xe4, 0x6c, 0x07, 0x7e, 0x14, 0x4d, 0x92, 0x34, 0x97, 0xe3, 0x28, 0x09, 0xe5, 0x7c, - 0x1c, 0xa4, 0xc9, 0x79, 0x1c, 0x05, 0x85, 0x79, 0xd1, 0xdf, 0xd6, 0xc2, 0x7d, 0x94, 0x3d, 0x33, - 0x22, 0xf6, 0x21, 0xb4, 0xf0, 0x76, 0x94, 0xc9, 0x0f, 0x29, 0xa3, 0xc4, 0x8b, 0x30, 0x53, 0x6b, - 0x21, 0xfb, 0x14, 0xda, 0x61, 0x9e, 0x66, 0xe3, 0x34, 0x23, 0x9c, 0xd7, 0x77, 0xee, 0x90, 0x3f, - 0x94, 0x08, 0x6c, 0xef, 0xe5, 0x69, 0x76, 0x9c, 0x71, 0x27, 0xa4, 0xbf, 0xfe, 0x03, 0x70, 0x34, - 0x87, 0xb9, 0xd0, 0x3c, 0x3a, 0x3e, 0x1a, 0x68, 0xec, 0x76, 0x0f, 0x0e, 0x7a, 0x16, 0xb2, 0xf6, - 0x76, 0x47, 0xbb, 0x3d, 0xdb, 0x9f, 0x83, 0x5b, 0x46, 0x65, 0xf6, 0x31, 0x86, 0x53, 0x8a, 0xea, - 0xc6, 0x29, 0xa9, 0x14, 0xa9, 0xa5, 0x57, 0xbc, 0x94, 0xa3, 0x1d, 0xd0, 0xf9, 0xca, 0x38, 0x4d, - 0x44, 0x3d, 0xb9, 0x6b, 0x2c, 0x55, 0x12, 0x98, 0xa7, 0xa6, 0x89, 0x34, 0xf9, 0x0e, 0x7d, 0xfb, - 0x7f, 0x6b, 0x83, 0xbb, 0x78, 0x48, 0x3f, 0x01, 0x6f, 0x5a, 0x9e, 0xc4, 0xb8, 0xfb, 0xda, 0xd2, - 0xf1, 0x78, 0x25, 0x67, 0x77, 0xc1, 0xbe, 0xbc, 0x32, 0x37, 0xe1, 0xa0, 0xd6, 0x57, 0x2f, 0xb9, - 0x7d, 0x79, 0x55, 0xc5, 0x8b, 0xd6, 0x77, 0xc6, 0x8b, 0x87, 0x70, 0x3b, 0x88, 0xa5, 0x48, 0xc6, - 0x95, 0xbb, 0x6b, 0x8b, 0x5e, 0x27, 0xf6, 0xc9, 0xc2, 0xe7, 0x4d, 0xcc, 0x6b, 0x57, 0x2f, 0xdb, - 0x03, 0x68, 0x85, 0x32, 0x2e, 0x44, 0xbd, 0x5c, 0x3b, 0xce, 0x45, 0x10, 0xcb, 0x3d, 0x64, 0x73, - 0x2d, 0x65, 0x5b, 0xe0, 0x96, 0xaf, 0xbc, 0x29, 0xd2, 0x28, 0xef, 0x2f, 0xc1, 0xe6, 0x0b, 0x69, - 0x85, 0x25, 0xd4, 0xb0, 0xf4, 0x3f, 0x83, 0xc6, 0x57, 0x2f, 0x87, 0xe6, 0xac, 0xd6, 0x2b, 0x67, - 0x2d, 0x11, 0xb5, 0x6b, 0x88, 0xfe, 0x77, 0x03, 0xda, 0xc6, 0xad, 0x71, 0xdf, 0xb3, 0x45, 0x8e, - 0x8a, 0x9f, 0xcb, 0x4f, 0xeb, 0x22, 0x3e, 0xd4, 0x4b, 0xfb, 0xc6, 0x77, 0x97, 0xf6, 0xec, 0x17, - 0xd0, 0xcd, 0xb4, 0xac, 0x1e, 0x51, 0xde, 0xa9, 0x8f, 0x31, 0x7f, 0x69, 0x5c, 0x27, 0xab, 0x08, - 0x74, 0x44, 0xaa, 0x86, 0x0a, 0x31, 0xa1, 0x2b, 0xea, 0xf2, 0x36, 0xd2, 0x23, 0x31, 0x79, 0x4d, - 0x5c, 0xf9, 0x1e, 0xe1, 0x01, 0x73, 0xf1, 0x34, 0xeb, 0x77, 0xc9, 0xe5, 0x31, 0xa4, 0xd4, 0xbd, - 0x7d, 0x6d, 0xd9, 0xdb, 0x7f, 0x0c, 0x5e, 0x90, 0x4e, 0xa7, 0x11, 0xc9, 0xd6, 0x4d, 0xae, 0x49, - 0x8c, 0x91, 0xf2, 0xff, 0xda, 0x82, 0xb6, 0x39, 0x2d, 0xeb, 0x40, 0x7b, 0x6f, 0xf0, 0x7c, 0xf7, - 0xf4, 0x00, 0x03, 0x0e, 0x80, 0xf3, 0x74, 0xff, 0x68, 0x97, 0xff, 0x49, 0xcf, 0x42, 0x07, 0xda, - 0x3f, 0x1a, 0xf5, 0x6c, 0xe6, 0x41, 0xeb, 0xf9, 0xc1, 0xf1, 0xee, 0xa8, 0xd7, 0x40, 0x5f, 0x7a, - 0x7a, 0x7c, 0x7c, 0xd0, 0x6b, 0xb2, 0x2e, 0xb8, 0x7b, 0xbb, 0xa3, 0xc1, 0x68, 0xff, 0x70, 0xd0, - 0x6b, 0xa1, 0xee, 0x8b, 0xc1, 0x71, 0xcf, 0xc1, 0x8f, 0xd3, 0xfd, 0xbd, 0x5e, 0x1b, 0xe5, 0x27, - 0xbb, 0xc3, 0xe1, 0x2f, 0x8f, 0xf9, 0x5e, 0xcf, 0xc5, 0x79, 0x87, 0x23, 0xbe, 0x7f, 0xf4, 0xa2, - 0xe7, 0xe1, 0xf7, 0xf1, 0xd3, 0x2f, 0x07, 0xcf, 0x46, 0x3d, 0xf0, 0x3f, 0x83, 0x4e, 0x0d, 0x41, - 0x1c, 0xcd, 0x07, 0xcf, 0x7b, 0xb7, 0x70, 0xc9, 0x97, 0xbb, 0x07, 0xa7, 0x83, 0x9e, 0xc5, 0xd6, - 0x01, 0xe8, 0x73, 0x7c, 0xb0, 0x7b, 0xf4, 0xa2, 0x67, 0xfb, 0xbf, 0x0b, 0xee, 0x69, 0x14, 0x3e, - 0x8d, 0xd3, 0xe0, 0x12, 0x0d, 0xe3, 0x4c, 0x28, 0x69, 0x5e, 0x69, 0xfa, 0xc6, 0x67, 0x84, 0x8c, - 0x52, 0x99, 0xbb, 0x37, 0x94, 0x7f, 0x04, 0xed, 0xd3, 0x28, 0x3c, 0x11, 0xc1, 0x25, 0x7b, 0x0f, - 0xe0, 0x0c, 0xc7, 0x8f, 0x55, 0xf4, 0x8d, 0x34, 0x11, 0xd4, 0x23, 0xce, 0x30, 0xfa, 0x46, 0xb2, - 0x0f, 0xc1, 0x21, 0xa2, 0xcc, 0xa7, 0xc8, 0x96, 0xcb, 0x35, 0xb9, 0x91, 0xf9, 0xc5, 0x62, 0xeb, - 0x54, 0xe9, 0xdf, 0x87, 0x66, 0x26, 0x82, 0x4b, 0x13, 0x4c, 0x3a, 0x66, 0x08, 0x2e, 0xc7, 0x49, - 0xc0, 0x1e, 0x82, 0x6b, 0xec, 0xa3, 0x9c, 0xb7, 0x53, 0x33, 0x24, 0xbe, 0x10, 0x2e, 0xdf, 0x5c, - 0x63, 0xe5, 0xe6, 0x7e, 0x06, 0x50, 0xb5, 0x4b, 0x6e, 0xc8, 0xed, 0xef, 0x40, 0x4b, 0xc4, 0x91, - 0x39, 0xbc, 0xc7, 0x35, 0xe1, 0x1f, 0x41, 0xa7, 0xd6, 0x64, 0x41, 0xb3, 0x11, 0x71, 0x3c, 0xbe, - 0x94, 0xd7, 0x8a, 0xc6, 0xba, 0xbc, 0x2d, 0xe2, 0xf8, 0x2b, 0x79, 0xad, 0x30, 0x3c, 0xeb, 0xfe, - 0x8c, 0xbd, 0x52, 0xf0, 0xd3, 0x50, 0xae, 0x85, 0xfe, 0x4f, 0xc1, 0xd1, 0x5d, 0x80, 0x9a, 0xd5, - 0x5a, 0xaf, 0x7d, 0xd4, 0xbe, 0x30, 0x7b, 0xa6, 0x9e, 0x01, 0xfb, 0xc4, 0xf4, 0x81, 0x94, 0xee, - 0x3a, 0x59, 0x55, 0xa2, 0xa7, 0x95, 0x4c, 0x0b, 0x88, 0x94, 0xfd, 0x3d, 0x70, 0xdf, 0xd8, 0x59, - 0x33, 0x00, 0xd8, 0x15, 0x00, 0x37, 0xf4, 0xda, 0xfc, 0x5f, 0x01, 0x54, 0xfd, 0x22, 0xe3, 0x44, - 0x7a, 0x16, 0x74, 0xa2, 0x47, 0x58, 0x94, 0x45, 0x71, 0x98, 0xcb, 0x64, 0xe9, 0xd4, 0x55, 0x87, - 0x69, 0x21, 0x67, 0x9b, 0xd0, 0xa4, 0x36, 0x58, 0xa3, 0x0a, 0x72, 0x8b, 0x1e, 0x18, 0x49, 0xfc, - 0x39, 0xac, 0xe9, 0xb7, 0x92, 0xcb, 0x3f, 0x9f, 0x49, 0xf5, 0xc6, 0x0c, 0xec, 0x1e, 0xc0, 0x22, - 0x24, 0x97, 0x0d, 0xbd, 0x1a, 0x07, 0x4d, 0xf9, 0x3c, 0x92, 0x71, 0x58, 0x9e, 0xc6, 0x50, 0x78, - 0xc9, 0xfa, 0x0d, 0x6d, 0xea, 0xae, 0x07, 0x11, 0xfe, 0xef, 0x43, 0xb7, 0x5c, 0x99, 0xda, 0x0a, - 0x9f, 0x2c, 0xde, 0x71, 0x8d, 0xb1, 0xae, 0x66, 0xb4, 0xca, 0x51, 0x1a, 0xca, 0xa7, 0x76, 0xdf, - 0x2a, 0x9f, 0x72, 0xff, 0xdf, 0x1b, 0xe5, 0x68, 0x53, 0x65, 0x2f, 0x65, 0x87, 0xd6, 0x6a, 0x76, - 0xb8, 0x9c, 0x69, 0xd9, 0xdf, 0x2b, 0xd3, 0xfa, 0x39, 0x78, 0x21, 0xa5, 0x1b, 0xd1, 0x55, 0x19, - 0x7e, 0x37, 0x56, 0x53, 0x0b, 0x93, 0x90, 0x44, 0x57, 0x92, 0x57, 0xca, 0xb8, 0x97, 0x22, 0xbd, - 0x94, 0x49, 0xf4, 0x0d, 0xb5, 0x11, 0xf0, 0xcc, 0x15, 0xa3, 0xea, 0xc9, 0xe8, 0xac, 0xc3, 0xf4, - 0x64, 0xca, 0xf6, 0x92, 0x53, 0xb5, 0x97, 0x10, 0xcf, 0x59, 0xa6, 0x64, 0x5e, 0x94, 0x79, 0xaa, - 0xa6, 0x16, 0x29, 0x9d, 0x67, 0x74, 0x31, 0xa5, 0x7b, 0x1f, 0xba, 0x49, 0x9a, 0x8c, 0x93, 0x59, - 0x1c, 0x63, 0x26, 0x6d, 0x3a, 0x89, 0x9d, 0x24, 0x4d, 0x8e, 0x0c, 0x8b, 0x3d, 0x82, 0xb7, 0xea, - 0x2a, 0xda, 0x9e, 0x3b, 0xba, 0x11, 0x51, 0xd3, 0x23, 0xab, 0xdf, 0x82, 0x5e, 0x7a, 0xf6, 0x2b, - 0x19, 0x14, 0x84, 0xd8, 0x98, 0x0c, 0xb9, 0xab, 0x1f, 0x61, 0xcd, 0x47, 0x88, 0x8e, 0xc4, 0x54, - 0xfa, 0x5f, 0x80, 0xb7, 0x00, 0xa1, 0x96, 0xce, 0x78, 0xd0, 0xda, 0x3f, 0xda, 0x1b, 0xfc, 0x71, - 0xcf, 0xc2, 0x88, 0xcd, 0x07, 0x2f, 0x07, 0x7c, 0x38, 0xe8, 0xd9, 0x18, 0x4d, 0xf7, 0x06, 0x07, - 0x83, 0xd1, 0xa0, 0xd7, 0xf8, 0xb2, 0xe9, 0xb6, 0x7b, 0x2e, 0x77, 0xe5, 0x3c, 0x8b, 0xa3, 0x20, - 0x2a, 0xfc, 0x21, 0x40, 0x95, 0x5a, 0x61, 0x5c, 0xa9, 0xd6, 0xd6, 0x37, 0xea, 0x16, 0x66, 0x55, - 0x4c, 0xfa, 0x8c, 0xa9, 0xd9, 0xaf, 0x4b, 0xfa, 0xb4, 0xdc, 0x3f, 0x05, 0xf7, 0x50, 0x64, 0xaf, - 0x14, 0x49, 0xdd, 0x45, 0x29, 0x3c, 0x33, 0x8d, 0x21, 0xf3, 0x14, 0x3f, 0x80, 0xb6, 0x09, 0x6d, - 0xc6, 0x6b, 0x96, 0xc2, 0x5e, 0x29, 0xf3, 0xff, 0xca, 0x82, 0x3b, 0x87, 0xe9, 0x95, 0x5c, 0x64, - 0x23, 0x27, 0xe2, 0x3a, 0x4e, 0x45, 0xf8, 0x1d, 0x86, 0xf8, 0x1e, 0x80, 0x4a, 0x67, 0x79, 0x20, - 0xc7, 0x93, 0x45, 0x3f, 0xca, 0xd3, 0x9c, 0x17, 0xa6, 0xf5, 0x2d, 0x55, 0x41, 0xc2, 0x86, 0x76, - 0x3e, 0xa4, 0x51, 0xf4, 0x23, 0x70, 0x8a, 0x79, 0x52, 0xb5, 0xbf, 0x5a, 0x05, 0x56, 0xa8, 0xfe, - 0x33, 0xf0, 0x46, 0x73, 0xaa, 0xdb, 0x66, 0x6a, 0xe9, 0x7d, 0xb5, 0xde, 0xf0, 0xbe, 0xda, 0x2b, - 0x51, 0xfa, 0xbf, 0x2c, 0xe8, 0xd4, 0xd2, 0x24, 0xf6, 0x3e, 0x34, 0x8b, 0x79, 0xb2, 0xdc, 0x37, - 0x2e, 0x17, 0xe1, 0x24, 0x42, 0x7b, 0xc3, 0xa2, 0x4e, 0x28, 0x15, 0x4d, 0x12, 0x19, 0x9a, 0x29, - 0xb1, 0xd0, 0xdb, 0x35, 0x2c, 0x76, 0x00, 0xb7, 0x75, 0x24, 0x29, 0x7b, 0x46, 0x65, 0x2a, 0xff, - 0xc1, 0x4a, 0x5a, 0xa6, 0x6b, 0xdb, 0x67, 0xa5, 0x96, 0xae, 0xde, 0xd7, 0x27, 0x4b, 0xcc, 0x8d, - 0x5d, 0x78, 0xfb, 0x06, 0xb5, 0x1f, 0xd4, 0xa6, 0xb8, 0x0f, 0x6b, 0x58, 0xd6, 0x47, 0x53, 0xa9, - 0x0a, 0x31, 0xcd, 0x28, 0x3f, 0x31, 0x2f, 0x41, 0x93, 0xdb, 0x85, 0xf2, 0x3f, 0x82, 0xee, 0x89, - 0x94, 0x39, 0x97, 0x2a, 0x4b, 0x13, 0xfd, 0x36, 0x2b, 0x3a, 0xb4, 0x79, 0x76, 0x0c, 0xe5, 0xff, - 0x19, 0x78, 0x98, 0x79, 0x3f, 0x15, 0x45, 0x70, 0xf1, 0x43, 0x32, 0xf3, 0x8f, 0xa0, 0x9d, 0x69, - 0x33, 0x31, 0x79, 0x74, 0x97, 0x62, 0x9c, 0x31, 0x1d, 0x5e, 0x0a, 0x7d, 0x0e, 0x8d, 0xa3, 0xd9, - 0xb4, 0xfe, 0x63, 0x4f, 0x53, 0xff, 0xd8, 0xb3, 0x54, 0xa1, 0xda, 0xcb, 0x15, 0x2a, 0x5a, 0xde, - 0x79, 0x9a, 0xff, 0x85, 0xc8, 0x43, 0x19, 0x9a, 0x32, 0xb8, 0x62, 0xf8, 0x5f, 0x43, 0xa7, 0xbc, - 0x99, 0xfd, 0x90, 0x7e, 0xcf, 0x21, 0xd3, 0xd8, 0x0f, 0x97, 0x2c, 0x45, 0x97, 0x91, 0x32, 0x09, - 0xf7, 0xcb, 0x2b, 0xd5, 0xc4, 0xf2, 0xca, 0xa6, 0x4d, 0xb2, 0xa8, 0x8d, 0x9f, 0x43, 0xb7, 0xcc, - 0x9d, 0x0f, 0x65, 0x21, 0xc8, 0xd8, 0xe2, 0x48, 0x26, 0x35, 0x43, 0x74, 0x35, 0x63, 0xa4, 0xde, - 0xd0, 0x90, 0xf5, 0xb7, 0xc1, 0x31, 0x96, 0xcc, 0xa0, 0x19, 0xa4, 0xa1, 0x76, 0xa0, 0x16, 0xa7, - 0x6f, 0x84, 0x63, 0xaa, 0x26, 0xe5, 0xe3, 0x39, 0x55, 0x13, 0xff, 0x1f, 0x6d, 0x58, 0x7b, 0x2a, - 0x82, 0xcb, 0x59, 0x56, 0xbe, 0x5e, 0xb5, 0x2a, 0xc7, 0x5a, 0xaa, 0x72, 0xde, 0xd0, 0x06, 0x7e, - 0x07, 0xda, 0xb3, 0x24, 0x9a, 0x97, 0xe9, 0x8b, 0xc7, 0x1d, 0x24, 0x75, 0xfb, 0x33, 0x4e, 0x03, - 0x2a, 0x6c, 0xc8, 0xe9, 0x3c, 0xbe, 0xa0, 0xa9, 0xb3, 0x10, 0x25, 0x81, 0x34, 0x58, 0x68, 0x62, - 0xb5, 0xa3, 0xea, 0xbc, 0xd2, 0x51, 0x7d, 0x0f, 0x40, 0x04, 0x81, 0x54, 0x6a, 0x5c, 0x55, 0x2e, - 0x9e, 0xe6, 0x7c, 0x25, 0xaf, 0x29, 0x3c, 0xc8, 0x20, 0x97, 0xc5, 0xb8, 0x6a, 0xd9, 0x79, 0x9a, - 0x83, 0xe2, 0x0f, 0x60, 0x4d, 0x49, 0xa5, 0xa2, 0x34, 0x19, 0xd3, 0x7b, 0x62, 0x5a, 0x78, 0x5d, - 0xc3, 0x1c, 0x21, 0x0f, 0xcd, 0x40, 0x24, 0x69, 0x72, 0x3d, 0x4d, 0x67, 0xaa, 0xfc, 0x41, 0x69, - 0xc1, 0xf0, 0xff, 0x14, 0xd6, 0x06, 0xf3, 0x8c, 0x9a, 0xf9, 0xdf, 0xf9, 0xde, 0xd7, 0xc0, 0xb4, - 0x97, 0xc0, 0x5c, 0x41, 0xac, 0x51, 0x22, 0xb6, 0xf3, 0x4f, 0x16, 0x34, 0xd1, 0xf2, 0xb1, 0x66, - 0xfc, 0x43, 0x29, 0xf2, 0xe2, 0x4c, 0x8a, 0x82, 0x2d, 0x59, 0xf9, 0xc6, 0x12, 0xe5, 0xdf, 0x7a, - 0x62, 0xb1, 0x6d, 0xfd, 0x3b, 0x41, 0xf9, 0xf3, 0xc7, 0x5a, 0xe9, 0x3f, 0xe4, 0x5f, 0xab, 0xfa, - 0x5b, 0xa4, 0xff, 0x65, 0x1a, 0x25, 0xcf, 0x74, 0xf3, 0x9c, 0xad, 0xfa, 0xdb, 0xea, 0x08, 0xf6, - 0x29, 0x38, 0xfb, 0x0a, 0x1d, 0xfb, 0x55, 0x55, 0x7a, 0x37, 0xea, 0x3e, 0xef, 0xdf, 0xda, 0xf9, - 0x87, 0x06, 0x34, 0xbf, 0x96, 0x79, 0xca, 0x7e, 0x0a, 0x6d, 0xd3, 0x1a, 0x63, 0xb5, 0x16, 0xd8, - 0x06, 0x25, 0x0e, 0x2b, 0x3d, 0x33, 0x5a, 0xa5, 0xa7, 0x9f, 0x9e, 0xaa, 0xac, 0x65, 0x55, 0xe7, - 0xee, 0x95, 0x4d, 0x7d, 0x01, 0xbd, 0x61, 0x91, 0x4b, 0x31, 0xad, 0xa9, 0x2f, 0x03, 0x75, 0x53, - 0x8d, 0x4c, 0x78, 0x7d, 0x02, 0x8e, 0x8e, 0x9e, 0x2b, 0x03, 0x56, 0xcb, 0x5d, 0x52, 0x7e, 0x08, - 0x9d, 0xe1, 0x45, 0x3a, 0x8b, 0xc3, 0xa1, 0xcc, 0xaf, 0x24, 0xab, 0xb5, 0xa7, 0x37, 0x6a, 0xdf, - 0xfe, 0x2d, 0xb6, 0x05, 0xa0, 0x03, 0xc4, 0x69, 0x14, 0x2a, 0xd6, 0x46, 0xd9, 0xd1, 0x6c, 0xaa, - 0x27, 0xad, 0x45, 0x0e, 0xad, 0x59, 0x0b, 0xa2, 0x6f, 0xd2, 0xfc, 0x1c, 0xd6, 0x9e, 0xd1, 0x23, - 0x73, 0x9c, 0xef, 0x9e, 0xa5, 0x79, 0xc1, 0x56, 0x5b, 0xd4, 0x1b, 0xab, 0x0c, 0xff, 0x16, 0x7b, - 0x02, 0xee, 0x28, 0xbf, 0xd6, 0xfa, 0x6f, 0x99, 0xb7, 0xa7, 0x5a, 0xef, 0x86, 0x53, 0xee, 0xfc, - 0xa6, 0x01, 0xce, 0x2f, 0xd3, 0xfc, 0x52, 0xe6, 0xec, 0x11, 0x38, 0xd4, 0x97, 0x30, 0x66, 0xb4, - 0xe8, 0x51, 0xdc, 0xb4, 0xd0, 0x87, 0xe0, 0x11, 0x28, 0x23, 0xa1, 0x2e, 0xf5, 0x55, 0xd1, 0xef, - 0xd8, 0x1a, 0x17, 0x9d, 0x95, 0xd2, 0xbd, 0xae, 0xeb, 0x8b, 0x5a, 0xf4, 0x62, 0x96, 0x9a, 0x05, - 0x1b, 0x6d, 0x5d, 0xf9, 0x0f, 0xd1, 0x34, 0x9f, 0x58, 0xec, 0x63, 0x68, 0x0e, 0xf5, 0x49, 0x51, - 0xa9, 0xfa, 0x55, 0x6f, 0x63, 0xbd, 0x64, 0x2c, 0x66, 0x7e, 0x0c, 0x8e, 0x4e, 0x59, 0xf4, 0x31, - 0x97, 0xf2, 0xf0, 0x8d, 0x5e, 0x9d, 0x65, 0x06, 0x7c, 0x04, 0x8e, 0x0e, 0x77, 0x7a, 0xc0, 0x52, - 0xe8, 0xdb, 0x28, 0xef, 0xc1, 0xbf, 0xc5, 0x3e, 0x06, 0x47, 0x3b, 0xb9, 0xd6, 0x5b, 0x72, 0x78, - 0x7d, 0x3a, 0x1d, 0x66, 0xb5, 0xd5, 0x72, 0x19, 0xc8, 0xa8, 0x96, 0xc9, 0xb0, 0xf2, 0x44, 0x37, - 0xb8, 0xde, 0x17, 0xb0, 0xb6, 0x94, 0xf5, 0xb0, 0x3e, 0xa1, 0x7c, 0x43, 0x22, 0xb4, 0x3a, 0xf8, - 0x69, 0xef, 0x5f, 0xbf, 0xbd, 0x67, 0xfd, 0xdb, 0xb7, 0xf7, 0xac, 0xff, 0xf8, 0xf6, 0x9e, 0xf5, - 0xeb, 0xff, 0xbc, 0x77, 0xeb, 0xcc, 0xa1, 0x7f, 0x7e, 0xf8, 0xfc, 0x7f, 0x03, 0x00, 0x00, 0xff, - 0xff, 0xc9, 0xad, 0x47, 0x4a, 0x40, 0x21, 0x00, 0x00, + 0x34, 0x4f, 0x46, 0x8a, 0xac, 0xab, 0xc9, 0x17, 0x34, 0xfb, 0x11, 0x78, 0x53, 0x31, 0x47, 0x3f, + 0xda, 0x0f, 0x8d, 0x75, 0x55, 0x0c, 0xf6, 0x3e, 0x34, 0x8a, 0x79, 0x42, 0x41, 0x09, 0xb3, 0x13, + 0x96, 0x1e, 0xa3, 0x79, 0x62, 0x3c, 0x8e, 0xa3, 0xac, 0x04, 0xd4, 0xad, 0x00, 0xed, 0x41, 0x23, + 0x88, 0x42, 0x4a, 0x4f, 0x1e, 0xc7, 0xcf, 0x8d, 0x3f, 0x80, 0xdb, 0x2b, 0x38, 0xd4, 0xef, 0x61, + 0x4d, 0x0f, 0xbb, 0x53, 0xbf, 0x87, 0x66, 0x1d, 0xfb, 0x5f, 0x37, 0xe0, 0xb6, 0x31, 0x86, 0x8b, + 0x28, 0x1b, 0x16, 0x68, 0xf6, 0x7d, 0x68, 0x53, 0xb4, 0x91, 0xb9, 0xb1, 0x89, 0x92, 0x64, 0xbf, + 0x07, 0x0e, 0x79, 0x60, 0x69, 0xa7, 0xf7, 0x2b, 0x54, 0x17, 0xc3, 0xb5, 0xdd, 0x9a, 0x2b, 0x31, + 0xea, 0xec, 0x27, 0xd0, 0xfa, 0x46, 0xe6, 0xa9, 0x8e, 0x9e, 0x9d, 0x9d, 0x7b, 0x37, 0x8d, 0xc3, + 0xbb, 0x35, 0xc3, 0xb4, 0xf2, 0x6f, 0x11, 0xfc, 0x0f, 0x31, 0x5e, 0x4e, 0xd3, 0x2b, 0x19, 0xf6, + 0xdb, 0xb4, 0xa3, 0xba, 0x7d, 0x94, 0xa2, 0x12, 0x6d, 0xb7, 0x42, 0x7b, 0x0f, 0x3a, 0xb5, 0xe3, + 0xdd, 0x80, 0xf4, 0xfd, 0x65, 0x8b, 0xf7, 0x16, 0x8e, 0x5c, 0x77, 0x9c, 0x3d, 0x80, 0xea, 0xb0, + 0xff, 0x5f, 0xf7, 0xf3, 0xff, 0xd2, 0x82, 0xdb, 0xcf, 0xd2, 0x24, 0x91, 0x54, 0x18, 0xe9, 0xab, + 0xab, 0xcc, 0xde, 0x7a, 0xad, 0xd9, 0x7f, 0x0c, 0x2d, 0x85, 0xca, 0x66, 0xf6, 0xb7, 0x6f, 0xb8, + 0x0b, 0xae, 0x35, 0x30, 0xcc, 0x4c, 0xc5, 0x7c, 0x9c, 0xc9, 0x24, 0x8c, 0x92, 0x49, 0x19, 0x66, + 0xa6, 0x62, 0x7e, 0xa2, 0x39, 0xfe, 0xaf, 0x2c, 0x70, 0xb4, 0xc7, 0x2c, 0x45, 0x6b, 0x6b, 0x39, + 0x5a, 0xff, 0x08, 0xbc, 0x2c, 0x97, 0x61, 0x14, 0x94, 0xab, 0x7a, 0xbc, 0x62, 0xa0, 0x71, 0x9e, + 0xa7, 0x79, 0x20, 0x69, 0x7a, 0x97, 0x6b, 0x02, 0xb9, 0x2a, 0x13, 0x81, 0x2e, 0xee, 0x1a, 0x5c, + 0x13, 0x18, 0xe3, 0xf5, 0xe5, 0xd0, 0xa5, 0xb8, 0xdc, 0x50, 0x58, 0x95, 0x52, 0xfe, 0xa3, 0x08, + 0xed, 0x91, 0xc8, 0x45, 0x06, 0x85, 0xe6, 0x7f, 0xb0, 0xa1, 0xbb, 0x17, 0xe5, 0x32, 0x28, 0x64, + 0x38, 0x08, 0x27, 0x34, 0x8b, 0x4c, 0x8a, 0xa8, 0xb8, 0x36, 0xc9, 0xc6, 0x50, 0x8b, 0x5a, 0xc0, + 0x5e, 0xae, 0x82, 0xf5, 0x5d, 0x34, 0xa8, 0x70, 0xd7, 0x04, 0xdb, 0x01, 0xd0, 0x55, 0x12, 0x15, + 0xef, 0xcd, 0xd7, 0x17, 0xef, 0x1e, 0xa9, 0xe1, 0x27, 0x02, 0xa4, 0xc7, 0x44, 0x3a, 0x11, 0x39, + 0x54, 0xd9, 0xcf, 0xd0, 0x90, 0xa9, 0xb8, 0x38, 0x93, 0x31, 0x19, 0x2a, 0x15, 0x17, 0x67, 0x32, + 0x5e, 0x94, 0x74, 0x6d, 0xbd, 0x1d, 0xfc, 0x66, 0x1f, 0x80, 0x9d, 0x66, 0x74, 0x78, 0xb3, 0x60, + 0xfd, 0x60, 0xdb, 0xc7, 0x19, 0xb7, 0xd3, 0x0c, 0xad, 0x40, 0x57, 0xaa, 0x7d, 0xcf, 0x18, 0x37, + 0x46, 0x17, 0xaa, 0xa6, 0xb8, 0x91, 0xf8, 0x77, 0xc1, 0x3e, 0xce, 0x58, 0x1b, 0x1a, 0xc3, 0xc1, + 0xa8, 0x77, 0x0b, 0x3f, 0xf6, 0x06, 0x07, 0x3d, 0xcb, 0xff, 0x17, 0x1b, 0xbc, 0xc3, 0x59, 0x21, + 0xd0, 0xa6, 0xd4, 0x9b, 0x2e, 0xf5, 0x5d, 0x70, 0x55, 0x21, 0x72, 0x8a, 0xd0, 0x3a, 0xac, 0xb4, + 0x89, 0x1e, 0x29, 0xf6, 0x11, 0xb4, 0x64, 0x38, 0x91, 0xa5, 0xb7, 0xf7, 0x56, 0xf7, 0xc9, 0xb5, + 0x98, 0x6d, 0x81, 0xa3, 0x82, 0x0b, 0x39, 0x15, 0xfd, 0x66, 0xa5, 0x38, 0x24, 0x8e, 0xce, 0xc0, + 0xdc, 0xc8, 0xd9, 0x0e, 0xfc, 0x20, 0x9a, 0x24, 0x69, 0x2e, 0xc7, 0x51, 0x12, 0xca, 0xf9, 0x38, + 0x48, 0x93, 0xf3, 0x38, 0x0a, 0x0a, 0x93, 0xd1, 0xdf, 0xd6, 0xc2, 0x7d, 0x94, 0x3d, 0x33, 0x22, + 0xf6, 0x21, 0xb4, 0xf0, 0x76, 0x94, 0xa9, 0x0f, 0xa9, 0xa2, 0xc4, 0x8b, 0x30, 0x53, 0x6b, 0x21, + 0xfb, 0x14, 0xda, 0x61, 0x9e, 0x66, 0xe3, 0x34, 0x23, 0x9c, 0xd7, 0x77, 0xee, 0x90, 0x3f, 0x94, + 0x08, 0x6c, 0xef, 0xe5, 0x69, 0x76, 0x9c, 0x71, 0x27, 0xa4, 0xbf, 0xfe, 0x03, 0x70, 0x34, 0x87, + 0xb9, 0xd0, 0x3c, 0x3a, 0x3e, 0x1a, 0x68, 0xec, 0x76, 0x0f, 0x0e, 0x7a, 0x16, 0xb2, 0xf6, 0x76, + 0x47, 0xbb, 0x3d, 0xdb, 0x9f, 0x83, 0x5b, 0x46, 0x65, 0xf6, 0x31, 0x86, 0x53, 0x8a, 0xea, 0xc6, + 0x29, 0xe9, 0x29, 0x52, 0x2b, 0xaf, 0x78, 0x29, 0x47, 0x3b, 0xa0, 0xf3, 0x95, 0x71, 0x9a, 0x88, + 0x7a, 0x71, 0xd7, 0x58, 0x7a, 0x49, 0x60, 0x9d, 0x9a, 0x26, 0xd2, 0xd4, 0x3b, 0xf4, 0xed, 0xff, + 0x9d, 0x0d, 0xee, 0x22, 0x91, 0x7e, 0x02, 0xde, 0xb4, 0x3c, 0x89, 0x71, 0xf7, 0xb5, 0xa5, 0xe3, + 0xf1, 0x4a, 0xce, 0xee, 0x82, 0x7d, 0x79, 0x65, 0x6e, 0xc2, 0x41, 0xad, 0xaf, 0x5e, 0x72, 0xfb, + 0xf2, 0xaa, 0x8a, 0x17, 0xad, 0xef, 0x8c, 0x17, 0x0f, 0xe1, 0x76, 0x10, 0x4b, 0x91, 0x8c, 0x2b, + 0x77, 0xd7, 0x16, 0xbd, 0x4e, 0xec, 0x93, 0x85, 0xcf, 0x9b, 0x98, 0xd7, 0xae, 0x32, 0xdb, 0x03, + 0x68, 0x85, 0x32, 0x2e, 0x44, 0xfd, 0xb9, 0x76, 0x9c, 0x8b, 0x20, 0x96, 0x7b, 0xc8, 0xe6, 0x5a, + 0xca, 0xb6, 0xc0, 0x2d, 0xb3, 0xbc, 0x79, 0xa4, 0x51, 0xdd, 0x5f, 0x82, 0xcd, 0x17, 0xd2, 0x0a, + 0x4b, 0xa8, 0x61, 0xe9, 0x7f, 0x06, 0x8d, 0xaf, 0x5e, 0x0e, 0xcd, 0x59, 0xad, 0x57, 0xce, 0x5a, + 0x22, 0x6a, 0xd7, 0x10, 0xfd, 0x9f, 0x06, 0xb4, 0x8d, 0x5b, 0xe3, 0xbe, 0x67, 0x8b, 0x1a, 0x15, + 0x3f, 0x97, 0x53, 0xeb, 0x22, 0x3e, 0xd4, 0x9f, 0xf6, 0x8d, 0xef, 0x7e, 0xda, 0xb3, 0x9f, 0x41, + 0x37, 0xd3, 0xb2, 0x7a, 0x44, 0x79, 0xa7, 0x3e, 0xc6, 0xfc, 0xa5, 0x71, 0x9d, 0xac, 0x22, 0xd0, + 0x11, 0xe9, 0x35, 0x54, 0x88, 0x09, 0x5d, 0x51, 0x97, 0xb7, 0x91, 0x1e, 0x89, 0xc9, 0x6b, 0xe2, + 0xca, 0x6f, 0x10, 0x1e, 0xb0, 0x16, 0x4f, 0xb3, 0x7e, 0x97, 0x5c, 0x1e, 0x43, 0x4a, 0xdd, 0xdb, + 0xd7, 0x96, 0xbd, 0xfd, 0x87, 0xe0, 0x05, 0xe9, 0x74, 0x1a, 0x91, 0x6c, 0xdd, 0xd4, 0x9a, 0xc4, + 0x18, 0x29, 0xff, 0xaf, 0x2d, 0x68, 0x9b, 0xd3, 0xb2, 0x0e, 0xb4, 0xf7, 0x06, 0xcf, 0x77, 0x4f, + 0x0f, 0x30, 0xe0, 0x00, 0x38, 0x4f, 0xf7, 0x8f, 0x76, 0xf9, 0x9f, 0xf4, 0x2c, 0x74, 0xa0, 0xfd, + 0xa3, 0x51, 0xcf, 0x66, 0x1e, 0xb4, 0x9e, 0x1f, 0x1c, 0xef, 0x8e, 0x7a, 0x0d, 0xf4, 0xa5, 0xa7, + 0xc7, 0xc7, 0x07, 0xbd, 0x26, 0xeb, 0x82, 0xbb, 0xb7, 0x3b, 0x1a, 0x8c, 0xf6, 0x0f, 0x07, 0xbd, + 0x16, 0xea, 0xbe, 0x18, 0x1c, 0xf7, 0x1c, 0xfc, 0x38, 0xdd, 0xdf, 0xeb, 0xb5, 0x51, 0x7e, 0xb2, + 0x3b, 0x1c, 0xfe, 0xfc, 0x98, 0xef, 0xf5, 0x5c, 0x9c, 0x77, 0x38, 0xe2, 0xfb, 0x47, 0x2f, 0x7a, + 0x1e, 0x7e, 0x1f, 0x3f, 0xfd, 0x72, 0xf0, 0x6c, 0xd4, 0x03, 0xff, 0x33, 0xe8, 0xd4, 0x10, 0xc4, + 0xd1, 0x7c, 0xf0, 0xbc, 0x77, 0x0b, 0x97, 0x7c, 0xb9, 0x7b, 0x70, 0x3a, 0xe8, 0x59, 0x6c, 0x1d, + 0x80, 0x3e, 0xc7, 0x07, 0xbb, 0x47, 0x2f, 0x7a, 0xb6, 0xff, 0xbb, 0xe0, 0x9e, 0x46, 0xe1, 0xd3, + 0x38, 0x0d, 0x2e, 0xd1, 0x30, 0xce, 0x84, 0x92, 0x26, 0x4b, 0xd3, 0x37, 0xa6, 0x11, 0x32, 0x4a, + 0x65, 0xee, 0xde, 0x50, 0xfe, 0x11, 0xb4, 0x4f, 0xa3, 0xf0, 0x44, 0x04, 0x97, 0xec, 0x3d, 0x80, + 0x33, 0x1c, 0x3f, 0x56, 0xd1, 0x37, 0xd2, 0x44, 0x50, 0x8f, 0x38, 0xc3, 0xe8, 0x1b, 0xc9, 0x3e, + 0x04, 0x87, 0x88, 0xb2, 0x9e, 0x22, 0x5b, 0x2e, 0xd7, 0xe4, 0x46, 0xe6, 0xff, 0xad, 0xb5, 0xd8, + 0x3b, 0x3d, 0xf5, 0xef, 0x43, 0x33, 0x13, 0xc1, 0xa5, 0x89, 0x26, 0x1d, 0x33, 0x06, 0xd7, 0xe3, + 0x24, 0x60, 0x0f, 0xc1, 0x35, 0x06, 0x52, 0x4e, 0xdc, 0xa9, 0x59, 0x12, 0x5f, 0x08, 0x97, 0xaf, + 0xae, 0xb1, 0x7c, 0x75, 0x78, 0x3c, 0x95, 0xc5, 0x11, 0xbd, 0xda, 0x1a, 0x18, 0x75, 0x34, 0xe5, + 0xff, 0x04, 0xa0, 0xea, 0xa3, 0xdc, 0x50, 0xf4, 0xdf, 0x81, 0x96, 0x88, 0x23, 0x83, 0x8a, 0xc7, + 0x35, 0xe1, 0x1f, 0x41, 0xa7, 0xd6, 0x7d, 0x41, 0x7b, 0x12, 0x71, 0x3c, 0xbe, 0x94, 0xd7, 0x8a, + 0xc6, 0xba, 0xbc, 0x2d, 0xe2, 0xf8, 0x2b, 0x79, 0xad, 0x30, 0x6e, 0xeb, 0xc6, 0x8d, 0xbd, 0xd2, + 0x09, 0xa0, 0xa1, 0x5c, 0x0b, 0xfd, 0x1f, 0x83, 0xa3, 0xdb, 0x03, 0x35, 0x73, 0xb6, 0x5e, 0x9b, + 0xed, 0xbe, 0x30, 0x7b, 0xa6, 0x66, 0x02, 0xfb, 0xc4, 0x34, 0x88, 0x94, 0x6e, 0x47, 0x59, 0x55, + 0x05, 0xa8, 0x95, 0x4c, 0x6f, 0x88, 0x94, 0xfd, 0x3d, 0x70, 0xdf, 0xd8, 0x72, 0x33, 0x00, 0xd8, + 0x15, 0x00, 0x37, 0x34, 0xe1, 0xfc, 0x5f, 0x00, 0x54, 0x8d, 0x24, 0xe3, 0x5d, 0x7a, 0x16, 0xf4, + 0xae, 0x47, 0xf8, 0x5a, 0x8b, 0xe2, 0x30, 0x97, 0xc9, 0xd2, 0xa9, 0xab, 0xd6, 0xd3, 0x42, 0xce, + 0x36, 0xa1, 0x49, 0xfd, 0xb1, 0x46, 0x15, 0xfd, 0x16, 0xcd, 0x31, 0x92, 0xf8, 0x73, 0x58, 0xd3, + 0x49, 0x94, 0xcb, 0x3f, 0x9f, 0x49, 0xf5, 0xc6, 0xd2, 0xec, 0x1e, 0xc0, 0x22, 0x56, 0x97, 0x9d, + 0xbe, 0x1a, 0x07, 0x8d, 0xe0, 0x3c, 0x92, 0x71, 0x58, 0x9e, 0xc6, 0x50, 0x78, 0xc9, 0x3a, 0xb9, + 0x36, 0x75, 0x3b, 0x84, 0x08, 0xff, 0xf7, 0xa1, 0x5b, 0xae, 0x4c, 0xfd, 0x86, 0x4f, 0x16, 0x09, + 0x5e, 0x63, 0xac, 0x9f, 0x39, 0x5a, 0xe5, 0x28, 0x0d, 0xe5, 0x53, 0xbb, 0x6f, 0x95, 0x39, 0xde, + 0xff, 0x8f, 0x46, 0x39, 0xda, 0x3c, 0xbf, 0x97, 0xca, 0x46, 0x6b, 0xb5, 0x6c, 0x5c, 0x2e, 0xc1, + 0xec, 0xdf, 0xa8, 0x04, 0xfb, 0x29, 0x78, 0x21, 0xd5, 0x21, 0xd1, 0x55, 0x19, 0x97, 0x37, 0x56, + 0x6b, 0x0e, 0x53, 0xa9, 0x44, 0x57, 0x92, 0x57, 0xca, 0xb8, 0x97, 0x22, 0xbd, 0x94, 0x49, 0xf4, + 0x0d, 0xf5, 0x17, 0xf0, 0xcc, 0x15, 0xa3, 0x6a, 0xd6, 0xe8, 0x72, 0xc4, 0x34, 0x6b, 0xca, 0xbe, + 0x93, 0x53, 0xf5, 0x9d, 0x10, 0xcf, 0x59, 0xa6, 0x64, 0x5e, 0x94, 0x05, 0xac, 0xa6, 0x16, 0xb5, + 0x9e, 0x67, 0x74, 0xb1, 0xd6, 0x7b, 0x1f, 0xba, 0x49, 0x9a, 0x8c, 0x93, 0x59, 0x1c, 0x63, 0x89, + 0x6d, 0x5a, 0x8c, 0x9d, 0x24, 0x4d, 0x8e, 0x0c, 0x8b, 0x3d, 0x82, 0xb7, 0xea, 0x2a, 0xda, 0x9e, + 0x3b, 0xba, 0x43, 0x51, 0xd3, 0x23, 0xab, 0xdf, 0x82, 0x5e, 0x7a, 0xf6, 0x0b, 0x19, 0x14, 0x84, + 0xd8, 0x98, 0x0c, 0xb9, 0xab, 0xb3, 0xb3, 0xe6, 0x23, 0x44, 0x47, 0x62, 0x2a, 0xfd, 0x2f, 0xc0, + 0x5b, 0x80, 0x50, 0xab, 0x73, 0x3c, 0x68, 0xed, 0x1f, 0xed, 0x0d, 0xfe, 0xb8, 0x67, 0x61, 0x28, + 0xe7, 0x83, 0x97, 0x03, 0x3e, 0x1c, 0xf4, 0x6c, 0x0c, 0xb3, 0x7b, 0x83, 0x83, 0xc1, 0x68, 0xd0, + 0x6b, 0x7c, 0xd9, 0x74, 0xdb, 0x3d, 0x97, 0xbb, 0x72, 0x9e, 0xc5, 0x51, 0x10, 0x15, 0xfe, 0x10, + 0xa0, 0xaa, 0xb9, 0x30, 0xde, 0x54, 0x6b, 0xeb, 0x1b, 0x75, 0x0b, 0xb3, 0x2a, 0x56, 0x83, 0xc6, + 0xd4, 0xec, 0xd7, 0x55, 0x83, 0x5a, 0xee, 0x9f, 0x82, 0x7b, 0x28, 0xb2, 0x57, 0x5e, 0x4f, 0xdd, + 0xc5, 0x1b, 0x79, 0x66, 0x3a, 0x46, 0x26, 0x47, 0x3f, 0x80, 0xb6, 0x09, 0x79, 0xc6, 0x6b, 0x96, + 0xc2, 0x61, 0x29, 0xf3, 0xff, 0xca, 0x82, 0x3b, 0x87, 0xe9, 0x95, 0x5c, 0x94, 0x29, 0x27, 0xe2, + 0x3a, 0x4e, 0x45, 0xf8, 0x1d, 0x86, 0xf8, 0x1e, 0x80, 0x4a, 0x67, 0x79, 0x20, 0xc7, 0x93, 0x45, + 0xa3, 0xca, 0xd3, 0x9c, 0x17, 0xa6, 0x27, 0x2e, 0x55, 0x41, 0xc2, 0x86, 0x76, 0x3e, 0xa4, 0x51, + 0xf4, 0x03, 0x70, 0x8a, 0x79, 0x52, 0xf5, 0xc5, 0x5a, 0x05, 0x3e, 0x5d, 0xfd, 0x67, 0xe0, 0x8d, + 0xe6, 0xf4, 0xa0, 0x9b, 0xa9, 0xa5, 0xc4, 0x6b, 0xbd, 0x21, 0xf1, 0xda, 0x2b, 0x89, 0xf7, 0xbf, + 0x2d, 0xe8, 0xd4, 0xea, 0x27, 0xf6, 0x3e, 0x34, 0x8b, 0x79, 0xb2, 0xdc, 0x50, 0x2e, 0x17, 0xe1, + 0x24, 0x42, 0x7b, 0xc3, 0xd7, 0x9e, 0x50, 0x2a, 0x9a, 0x24, 0x32, 0x34, 0x53, 0xe2, 0x0b, 0x70, + 0xd7, 0xb0, 0xd8, 0x01, 0xdc, 0xd6, 0x91, 0xa4, 0x6c, 0x26, 0x95, 0x35, 0xfe, 0x07, 0x2b, 0xf5, + 0x9a, 0x7e, 0xf4, 0x3e, 0x2b, 0xb5, 0xf4, 0xb3, 0x7e, 0x7d, 0xb2, 0xc4, 0xdc, 0xd8, 0x85, 0xb7, + 0x6f, 0x50, 0xfb, 0x5e, 0xfd, 0x8b, 0xfb, 0xb0, 0x86, 0xef, 0xfd, 0x68, 0x2a, 0x55, 0x21, 0xa6, + 0x19, 0x15, 0x2e, 0x26, 0x13, 0x34, 0xb9, 0x5d, 0x28, 0xff, 0x23, 0xe8, 0x9e, 0x48, 0x99, 0x73, + 0xa9, 0xb2, 0x34, 0xd1, 0x49, 0x5b, 0xd1, 0xa1, 0x4d, 0xda, 0x31, 0x94, 0xff, 0x67, 0xe0, 0x61, + 0x49, 0xfe, 0x54, 0x14, 0xc1, 0xc5, 0xf7, 0x29, 0xd9, 0x3f, 0x82, 0x76, 0xa6, 0xcd, 0xc4, 0x14, + 0xd8, 0x5d, 0x8a, 0x71, 0xc6, 0x74, 0x78, 0x29, 0xf4, 0x39, 0x34, 0x8e, 0x66, 0xd3, 0xfa, 0xaf, + 0x40, 0x4d, 0xfd, 0x2b, 0xd0, 0xd2, 0xd3, 0xd5, 0x5e, 0x7e, 0xba, 0xa2, 0xe5, 0x9d, 0xa7, 0xf9, + 0x5f, 0x88, 0x3c, 0x94, 0xa1, 0x79, 0x1f, 0x57, 0x0c, 0xff, 0x6b, 0xe8, 0x94, 0x37, 0xb3, 0x1f, + 0xd2, 0x0f, 0x3d, 0x64, 0x1a, 0xfb, 0xe1, 0x92, 0xa5, 0xe8, 0xf7, 0xa5, 0x4c, 0xc2, 0xfd, 0xf2, + 0x4a, 0x35, 0xb1, 0xbc, 0xb2, 0xe9, 0x9f, 0x2c, 0x1e, 0xcd, 0xcf, 0xa1, 0x5b, 0x16, 0xd5, 0x87, + 0xb2, 0x10, 0x64, 0x6c, 0x71, 0x24, 0x93, 0x9a, 0x21, 0xba, 0x9a, 0x31, 0x52, 0x6f, 0xe8, 0xd4, + 0xfa, 0xdb, 0xe0, 0x18, 0x4b, 0x66, 0xd0, 0x0c, 0xd2, 0x50, 0x3b, 0x50, 0x8b, 0xd3, 0x37, 0xc2, + 0x31, 0x55, 0x93, 0x32, 0x79, 0x4e, 0xd5, 0xc4, 0xff, 0x27, 0x1b, 0xd6, 0x9e, 0x8a, 0xe0, 0x72, + 0x96, 0x95, 0xd9, 0xab, 0xf6, 0xfc, 0xb1, 0x96, 0x9e, 0x3f, 0x6f, 0xe8, 0x0f, 0xbf, 0x03, 0xed, + 0x59, 0x12, 0xcd, 0xcb, 0xb2, 0xc6, 0xe3, 0x0e, 0x92, 0xba, 0x2f, 0x1a, 0xa7, 0x01, 0xbd, 0x78, + 0xc8, 0xe9, 0x3c, 0xbe, 0xa0, 0xa9, 0xe5, 0x10, 0x25, 0x81, 0x34, 0x58, 0x68, 0x62, 0xb5, 0xd5, + 0xea, 0xbc, 0xd2, 0x6a, 0x7d, 0x0f, 0x40, 0x04, 0x81, 0x54, 0x6a, 0x5c, 0x3d, 0x69, 0x3c, 0xcd, + 0xf9, 0x4a, 0x5e, 0x53, 0x78, 0x90, 0x41, 0x2e, 0x8b, 0x71, 0xd5, 0xcb, 0xf3, 0x34, 0x07, 0xc5, + 0x1f, 0xc0, 0x9a, 0x92, 0x4a, 0x45, 0x69, 0x32, 0xa6, 0x7c, 0x62, 0x7a, 0x7b, 0x5d, 0xc3, 0x1c, + 0x21, 0x0f, 0xcd, 0x40, 0x24, 0x69, 0x72, 0x3d, 0x4d, 0x67, 0xaa, 0xfc, 0xa5, 0x69, 0xc1, 0xf0, + 0xff, 0x14, 0xd6, 0x06, 0xf3, 0x8c, 0xba, 0xfc, 0xdf, 0x99, 0xef, 0x6b, 0x60, 0xda, 0x4b, 0x60, + 0xae, 0x20, 0xd6, 0x28, 0x11, 0xdb, 0xf9, 0x67, 0x0b, 0x9a, 0x68, 0xf9, 0xf8, 0x98, 0xfc, 0x43, + 0x29, 0xf2, 0xe2, 0x4c, 0x8a, 0x82, 0x2d, 0x59, 0xf9, 0xc6, 0x12, 0xe5, 0xdf, 0x7a, 0x62, 0xb1, + 0x6d, 0xfd, 0x03, 0x42, 0xf9, 0xbb, 0xc8, 0x5a, 0xe9, 0x3f, 0xe4, 0x5f, 0xab, 0xfa, 0x5b, 0xa4, + 0xff, 0x65, 0x1a, 0x25, 0xcf, 0x74, 0x57, 0x9d, 0xad, 0xfa, 0xdb, 0xea, 0x08, 0xf6, 0x29, 0x38, + 0xfb, 0x0a, 0x1d, 0xfb, 0x55, 0x55, 0xca, 0x1b, 0x75, 0x9f, 0xf7, 0x6f, 0xed, 0xfc, 0x63, 0x03, + 0x9a, 0x5f, 0xcb, 0x3c, 0x65, 0x3f, 0x86, 0xb6, 0xe9, 0x99, 0xb1, 0x5a, 0x6f, 0x6c, 0x83, 0x0a, + 0x87, 0x95, 0x66, 0x1a, 0xad, 0xd2, 0xd3, 0xa9, 0xa7, 0x7a, 0xef, 0xb2, 0xaa, 0xa5, 0xf7, 0xca, + 0xa6, 0xbe, 0x80, 0xde, 0xb0, 0xc8, 0xa5, 0x98, 0xd6, 0xd4, 0x97, 0x81, 0xba, 0xe9, 0xf1, 0x4c, + 0x78, 0x7d, 0x02, 0x8e, 0x8e, 0x9e, 0x2b, 0x03, 0x56, 0xdf, 0xc1, 0xa4, 0xfc, 0x10, 0x3a, 0xc3, + 0x8b, 0x74, 0x16, 0x87, 0x43, 0x99, 0x5f, 0x49, 0x56, 0xeb, 0x5b, 0x6f, 0xd4, 0xbe, 0xfd, 0x5b, + 0x6c, 0x0b, 0x40, 0x07, 0x88, 0xd3, 0x28, 0x54, 0xac, 0x8d, 0xb2, 0xa3, 0xd9, 0x54, 0x4f, 0x5a, + 0x8b, 0x1c, 0x5a, 0xb3, 0x16, 0x44, 0xdf, 0xa4, 0xf9, 0x39, 0xac, 0x3d, 0xa3, 0x24, 0x73, 0x9c, + 0xef, 0x9e, 0xa5, 0x79, 0xc1, 0x56, 0x7b, 0xd7, 0x1b, 0xab, 0x0c, 0xff, 0x16, 0x7b, 0x02, 0xee, + 0x28, 0xbf, 0xd6, 0xfa, 0x6f, 0x99, 0xdc, 0x53, 0xad, 0x77, 0xc3, 0x29, 0x77, 0x7e, 0xd5, 0x00, + 0xe7, 0xe7, 0x69, 0x7e, 0x29, 0x73, 0xf6, 0x08, 0x1c, 0x6a, 0x58, 0x18, 0x33, 0x5a, 0x34, 0x2f, + 0x6e, 0x5a, 0xe8, 0x43, 0xf0, 0x08, 0x94, 0x91, 0x50, 0x97, 0xfa, 0xaa, 0xe8, 0x07, 0x6e, 0x8d, + 0x8b, 0xae, 0x4a, 0xe9, 0x5e, 0xd7, 0xf5, 0x45, 0x2d, 0x9a, 0x34, 0x4b, 0x5d, 0x84, 0x8d, 0xb6, + 0x6e, 0x09, 0x0c, 0xd1, 0x34, 0x9f, 0x58, 0xec, 0x63, 0x68, 0x0e, 0xf5, 0x49, 0x51, 0xa9, 0xfa, + 0xb9, 0x6f, 0x63, 0xbd, 0x64, 0x2c, 0x66, 0x7e, 0x0c, 0x8e, 0x2e, 0x59, 0xf4, 0x31, 0x97, 0xea, + 0xf0, 0x8d, 0x5e, 0x9d, 0x65, 0x06, 0x7c, 0x04, 0x8e, 0x0e, 0x77, 0x7a, 0xc0, 0x52, 0xe8, 0xdb, + 0x28, 0xef, 0xc1, 0xbf, 0xc5, 0x3e, 0x06, 0x47, 0x3b, 0xb9, 0xd6, 0x5b, 0x72, 0x78, 0x7d, 0x3a, + 0x1d, 0x66, 0xb5, 0xd5, 0x72, 0x19, 0xc8, 0xa8, 0x56, 0xc9, 0xb0, 0xf2, 0x44, 0x37, 0xb8, 0xde, + 0x17, 0xb0, 0xb6, 0x54, 0xf5, 0xb0, 0x3e, 0xa1, 0x7c, 0x43, 0x21, 0xb4, 0x3a, 0xf8, 0x69, 0xef, + 0xdf, 0xbe, 0xbd, 0x67, 0xfd, 0xfb, 0xb7, 0xf7, 0xac, 0xff, 0xfc, 0xf6, 0x9e, 0xf5, 0xcb, 0xff, + 0xba, 0x77, 0xeb, 0xcc, 0xa1, 0xff, 0x8a, 0xf8, 0xfc, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x11, + 0x23, 0x23, 0x2a, 0x59, 0x21, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -6598,6 +6607,23 @@ func (m *PostingList) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintPb(dAtA, i, uint64(m.CommitTs)) } + if len(m.Splits) > 0 { + dAtA23 := make([]byte, len(m.Splits)*10) + var j22 int + for _, num := range m.Splits { + for num >= 1<<7 { + dAtA23[j22] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j22++ + } + dAtA23[j22] = uint8(num) + j22++ + } + dAtA[i] = 0x22 + i++ + i = encodeVarintPb(dAtA, i, uint64(j22)) + i += copy(dAtA[i:], dAtA23[:j22]) + } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) } @@ -6831,11 +6857,11 @@ func (m *FilterTree) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintPb(dAtA, i, uint64(m.Func.Size())) - n22, err22 := m.Func.MarshalTo(dAtA[i:]) - if err22 != nil { - return 0, err22 + n24, err24 := m.Func.MarshalTo(dAtA[i:]) + if err24 != nil { + return 0, err24 } - i += n22 + i += n24 } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) @@ -7135,11 +7161,11 @@ func (m *MapEntry) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintPb(dAtA, i, uint64(m.Posting.Size())) - n23, err23 := m.Posting.MarshalTo(dAtA[i:]) - if err23 != nil { - return 0, err23 + n25, err25 := m.Posting.MarshalTo(dAtA[i:]) + if err25 != nil { + return 0, err25 } - i += n23 + i += n25 } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) @@ -7289,21 +7315,21 @@ func (m *TxnTimestamps) MarshalTo(dAtA []byte) (int, error) { var l int _ = l if len(m.Ts) > 0 { - dAtA25 := make([]byte, len(m.Ts)*10) - var j24 int + dAtA27 := make([]byte, len(m.Ts)*10) + var j26 int for _, num := range m.Ts { for num >= 1<<7 { - dAtA25[j24] = uint8(uint64(num)&0x7f | 0x80) + dAtA27[j26] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j24++ + j26++ } - dAtA25[j24] = uint8(num) - j24++ + dAtA27[j26] = uint8(num) + j26++ } dAtA[i] = 0xa i++ - i = encodeVarintPb(dAtA, i, uint64(j24)) - i += copy(dAtA[i:], dAtA25[:j24]) + i = encodeVarintPb(dAtA, i, uint64(j26)) + i += copy(dAtA[i:], dAtA27[:j26]) } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) @@ -7361,21 +7387,21 @@ func (m *RaftBatch) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintPb(dAtA, i, uint64(m.Context.Size())) - n26, err26 := m.Context.MarshalTo(dAtA[i:]) - if err26 != nil { - return 0, err26 + n28, err28 := m.Context.MarshalTo(dAtA[i:]) + if err28 != nil { + return 0, err28 } - i += n26 + i += n28 } if m.Payload != nil { dAtA[i] = 0x12 i++ i = encodeVarintPb(dAtA, i, uint64(m.Payload.Size())) - n27, err27 := m.Payload.MarshalTo(dAtA[i:]) - if err27 != nil { - return 0, err27 + n29, err29 := m.Payload.MarshalTo(dAtA[i:]) + if err29 != nil { + return 0, err29 } - i += n27 + i += n29 } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) @@ -8482,6 +8508,13 @@ func (m *PostingList) Size() (n int) { if m.CommitTs != 0 { n += 1 + sovPb(uint64(m.CommitTs)) } + if len(m.Splits) > 0 { + l = 0 + for _, e := range m.Splits { + l += sovPb(uint64(e)) + } + n += 1 + sovPb(uint64(l)) + l + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -14161,6 +14194,82 @@ func (m *PostingList) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPb + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Splits = append(m.Splits, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPb + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthPb + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthPb + } + 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.Splits) == 0 { + m.Splits = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPb + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Splits = append(m.Splits, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Splits", wireType) + } default: iNdEx = preIndex skippy, err := skipPb(dAtA[iNdEx:]) diff --git a/worker/draft.go b/worker/draft.go index 3deb954f420..54492d06f97 100644 --- a/worker/draft.go +++ b/worker/draft.go @@ -904,9 +904,16 @@ func (n *node) rollupLists(readTs uint64) error { return nil, err } atomic.AddUint64(&numKeys, 1) - kv, err := l.MarshalToKv() - addTo(key, int64(kv.Size())) - return listWrap(kv), err + kvs, err := l.Rollup() + + // If there are multiple keys, the posting list was split into multiple + // parts. The key of the first part is the right key to use for tablet + // size calculations. + for _, kv := range kvs { + addTo(kvs[0].Key, int64(kv.Size())) + } + + return &bpb.KVList{Kv: kvs}, err } stream.Send = func(list *bpb.KVList) error { return writer.Send(&pb.KVS{Kv: list.Kv}) diff --git a/worker/predicate_move.go b/worker/predicate_move.go index 83efe4f2aae..04c63e2be0e 100644 --- a/worker/predicate_move.go +++ b/worker/predicate_move.go @@ -259,12 +259,12 @@ func movePredicateHelper(ctx context.Context, in *pb.MovePredicatePayload) error if err != nil { return nil, err } - kv, err := l.MarshalToKv() - if kv != nil { + kvs, err := l.Rollup() + for _, kv := range kvs { // Let's set all of them at this move timestamp. kv.Version = in.TxnTs } - return listWrap(kv), err + return &bpb.KVList{Kv: kvs}, err } stream.Send = func(list *bpb.KVList) error { return s.Send(&pb.KVS{Kv: list.Kv}) diff --git a/x/keys.go b/x/keys.go index fa1f7f16327..ae1b5ebf6c9 100644 --- a/x/keys.go +++ b/x/keys.go @@ -130,6 +130,7 @@ type ParsedKey struct { byteType byte Attr string Uid uint64 + StartUid uint64 Term string Count uint32 bytePrefix byte @@ -279,6 +280,15 @@ func PredicatePrefix(predicate string) []byte { return buf } +// GetSplitKey takes a data key baseKey and generates the key of the list split +// that starts at startUid. +func GetSplitKey(baseKey []byte, startUid uint64) []byte { + keyCopy := make([]byte, len(baseKey)+8) + copy(keyCopy, baseKey) + binary.BigEndian.PutUint64(keyCopy[len(baseKey):], startUid) + return keyCopy +} + // Parse would parse the key. ParsedKey does not reuse the key slice, so the key slice can change // without affecting the contents of ParsedKey. func Parse(key []byte) *ParsedKey { @@ -309,6 +319,25 @@ func Parse(key []byte) *ParsedKey { return nil } p.Uid = binary.BigEndian.Uint64(k) + + if len(k) == 8 { + return p + } + k = k[8:] + if len(k) < 8 { + if Config.DebugMode { + fmt.Printf("Error: StartUid length < 8 for key: %q, parsed key: %+v\n", key, p) + } + return nil + } + p.StartUid = binary.BigEndian.Uint64(k) + if p.StartUid == 0 { + if Config.DebugMode { + fmt.Printf("Error: StartUid must be greater than 0 for key %q, parsed key: %+v\n", + key, p) + } + return nil + } case ByteIndex: p.Term = string(k) case ByteCount, ByteCountRev: diff --git a/x/keys_test.go b/x/keys_test.go index d8c366f8461..efaeae77621 100644 --- a/x/keys_test.go +++ b/x/keys_test.go @@ -34,6 +34,7 @@ func TestDataKey(t *testing.T) { require.True(t, pk.IsData()) require.Equal(t, sattr, pk.Attr) require.Equal(t, uid, pk.Uid) + require.Equal(t, uint64(0), pk.StartUid) } keys := make([]string, 0, 1024) @@ -50,6 +51,22 @@ func TestDataKey(t *testing.T) { } } +func TestParseKeysWithStartUid(t *testing.T) { + var uid uint64 + startUid := uint64(1024) + for uid = 0; uid < 1001; uid++ { + sattr := fmt.Sprintf("attr:%d", uid) + key := DataKey(sattr, uid) + key = GetSplitKey(key, startUid) + pk := Parse(key) + + require.True(t, pk.IsData()) + require.Equal(t, sattr, pk.Attr) + require.Equal(t, uid, pk.Uid) + require.Equal(t, startUid, pk.StartUid) + } +} + func TestIndexKey(t *testing.T) { var uid uint64 for uid = 0; uid < 1001; uid++ {